From 2358772d25b5bec83e1d48946bd97c924d519176 Mon Sep 17 00:00:00 2001 From: SimpleTest Date: Mon, 20 Jul 2026 16:18:15 +0300 Subject: [PATCH] fix: harden security concurrency and client boundaries --- .dockerignore | 18 + .env.example | 4 +- .gitignore | 6 + README.md | 2 +- android/.dockerignore | 11 + android/README.md | 7 +- android/app/build.gradle.kts | 6 + .../mobile/AndroidClientInstrumentedTest.java | 145 +- .../mobile/TrackingProcessDeathProbeTest.java | 18 +- .../org/whoneedhelp/mobile/MainActivity.java | 79 +- .../org/whoneedhelp/mobile/TrustedOrigin.java | 17 + .../whoneedhelp/mobile/TrustedOriginTest.java | 7 + assets/js/app.js | 45 +- assets/js/hooks.js | 48 +- compose.external-boundaries.yaml | 1 + config/config.exs | 4 + config/prod.exs | 1 + config/runtime.exs | 66 +- docs/architecture.md | 12 +- docs/operations.md | 5 + e2e/tests/auth-settings.spec.ts | 6 +- e2e/tests/helpers.ts | 4 +- lib/mix/tasks/wnh.staging_android_e2e.ex | 4 +- lib/who_need_help/accounts.ex | 88 +- lib/who_need_help/accounts/social_identity.ex | 8 +- lib/who_need_help/accounts/user.ex | 7 +- lib/who_need_help/accounts/user_token.ex | 12 + lib/who_need_help/activities.ex | 368 +++-- lib/who_need_help/activities/activity.ex | 22 +- lib/who_need_help/catalog.ex | 11 +- lib/who_need_help/catalog/category.ex | 4 + lib/who_need_help/help.ex | 358 +++-- lib/who_need_help/help/help_request.ex | 17 +- lib/who_need_help/messaging.ex | 48 +- lib/who_need_help/tracking/position.ex | 16 +- lib/who_need_help/trust.ex | 176 ++- lib/who_need_help/trust/rate_limiter.ex | 6 +- lib/who_need_help/workers/expire_requests.ex | 4 +- .../components/core_components.ex | 22 +- .../controllers/metrics_controller.ex | 1 + .../user_registration_controller.ex | 112 +- .../controllers/user_session_controller.ex | 55 +- .../user_session_html/confirm.html.heex | 70 - .../user_session_html/new.html.heex | 108 +- .../controllers/user_settings_controller.ex | 89 +- .../confirm_email.html.heex | 26 + lib/who_need_help_web/endpoint.ex | 6 +- .../live/activity_live/index.ex | 57 +- .../live/activity_live/new.ex | 5 +- .../live/activity_live/show.ex | 43 +- lib/who_need_help_web/live/moderation_live.ex | 6 +- lib/who_need_help_web/live/profile_live.ex | 25 +- .../live/request_live/index.ex | 42 +- .../live/request_live/new.ex | 5 +- .../live/request_live/show.ex | 41 +- lib/who_need_help_web/router.ex | 14 +- lib/who_need_help_web/user_auth.ex | 3 +- lib/who_need_help_web/validation_messages.ex | 3 +- priv/gettext/default.pot | 1291 ++++++++-------- priv/gettext/en/LC_MESSAGES/default.po | 1337 +++++++++-------- priv/gettext/en/LC_MESSAGES/errors.po | 25 +- priv/gettext/errors.pot | 25 +- priv/gettext/ru/LC_MESSAGES/default.po | 1295 ++++++++-------- priv/gettext/ru/LC_MESSAGES/errors.po | 25 +- priv/gettext/uk/LC_MESSAGES/default.po | 1295 ++++++++-------- priv/gettext/uk/LC_MESSAGES/errors.po | 25 +- scripts/android-instrumentation-test.sh | 22 +- scripts/android-matrix-test.sh | 4 +- scripts/load-resilience-run.sh | 53 +- scripts/quality.sh | 11 +- test/who_need_help/accounts_test.exs | 140 +- test/who_need_help/activities_test.exs | 103 +- test/who_need_help/mutual_aid_flow_test.exs | 85 ++ test/who_need_help/trust_safety_test.exs | 34 +- .../controllers/metrics_controller_test.exs | 1 + .../controllers/page_controller_test.exs | 20 + .../user_registration_controller_test.exs | 44 +- .../user_session_controller_test.exs | 50 +- .../user_settings_controller_test.exs | 89 +- .../live/mutual_aid_live_test.exs | 171 +++ 80 files changed, 5200 insertions(+), 3339 deletions(-) delete mode 100644 lib/who_need_help_web/controllers/user_session_html/confirm.html.heex create mode 100644 lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex diff --git a/.dockerignore b/.dockerignore index 42d01ba..e0a42b3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -45,10 +45,28 @@ erl_crash.dump /priv/static/assets/ /priv/static/cache_manifest.json /android/.gradle/ +/android/.kotlin/ +/android/.idea/ +/android/.cxx/ /android/app/build/ /android/build/ /android/dist/ /android/dist-*/ +/android/local.properties +/android/*.apk +/android/*.aab +/android/*.jks +/android/*.keystore +/android/**/*.apk +/android/**/*.aab +/android/**/*.jks +/android/**/*.keystore +*.key +*.pem +*.p12 +*.pfx +/.direnv/ +/.envrc /.tools/ /.playwright-cli/ /output/ diff --git a/.env.example b/.env.example index 50818b6..a2ea219 100644 --- a/.env.example +++ b/.env.example @@ -29,8 +29,8 @@ WNH_BASE_URL= # production capacity recommendations. WNH_TRACKING_MIN_TIME_MS=5000 WNH_TRACKING_HTTP_TIMEOUT_MS=15000 -WNH_ANDROID_TEST_API_MATRIX=30 34 37.0 -WNH_ANDROID_TEST_DATA_PARTITION_SIZE=4G +WNH_ANDROID_TEST_API_MATRIX="24 30 34 37.0" +WNH_ANDROID_TEST_DATA_PARTITION_SIZE=1G # Public raster tile template used by MapLibre. Use a provider whose policy and # capacity match the deployment before a public launch. MAP_TILE_URL=https://tile.openstreetmap.org/{z}/{x}/{y}.png diff --git a/.gitignore b/.gitignore index 970f56f..be3987f 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,9 @@ __pycache__/ *.aab *.jks *.keystore +*.key +*.pem +*.p12 +*.pfx +/.direnv/ +/.envrc diff --git a/README.md b/README.md index 4e3685b..9a78a3a 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,7 @@ is not recreated. The Android device suite builds dedicated debug and instrumentation APKs, boots a fresh emulator in an isolated container without external networking, and serves its HTTP fixture only on device loopback. Run the default API 37 -probe or the complete API 30/34/37 matrix: +probe or the complete minimum/current API 24/30/34/37 matrix: ```bash ./scripts/android-instrumentation-test.sh diff --git a/android/.dockerignore b/android/.dockerignore index dfb3b28..01946df 100644 --- a/android/.dockerignore +++ b/android/.dockerignore @@ -1,6 +1,17 @@ .gradle +.kotlin +.idea +.cxx app/build build dist dist-* local.properties +*.apk +*.aab +*.jks +*.keystore +**/*.apk +**/*.aab +**/*.jks +**/*.keystore diff --git a/android/README.md b/android/README.md index 319d94f..107b01b 100644 --- a/android/README.md +++ b/android/README.md @@ -7,12 +7,17 @@ service with a persistent notification and Stop action, so it can continue while the Activity is minimized without requesting Android's background-location permission. +The native tracking bridge uses `WebViewCompat.addWebMessageListener` with the +exact configured origin and rejects messages outside the main frame. It does +not expose a legacy `addJavascriptInterface` object to every frame. + ## Verified build configuration - Android Gradle Plugin 9.3.0 - Gradle 9.6.1 - Android SDK Command-line Tools 22.0 - Android CLI 1.0.15857036 (embedded in the locked Command-line Tools archive) +- AndroidX WebKit 1.16.0 - compileSdk / targetSdk 37 - Build Tools 37.0.0 - Java source and bytecode level 17 @@ -88,7 +93,7 @@ report. ## Automated device tests -Run the default API 37 suite or the complete API 30/34/37 matrix from the +Run the default API 37 suite or the complete API 24/30/34/37 matrix from the repository root: ```sh diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 7798250..b51c733 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -20,6 +20,9 @@ fun manifestOrigin(value: String): URI? = (uri.scheme == "http" || uri.scheme == "https") && !uri.host.isNullOrBlank() } +fun isOriginPath(uri: URI): Boolean = + uri.path.isNullOrEmpty() || uri.path == "/" + val debugManifestOrigin = manifestOrigin(debugBaseUrl.get()) val releaseManifestOrigin = manifestOrigin(releaseBaseUrl.get()) @@ -119,6 +122,7 @@ tasks.matching { it.name == "preReleaseBuild" || it.name == "preStagingBuild" }. uri.scheme != "https" || uri.host.isNullOrBlank() || uri.userInfo != null || + !isOriginPath(uri) || uri.query != null || uri.fragment != null ) { @@ -142,6 +146,7 @@ tasks.matching { it.name == "preDebugBuild" }.configureEach { (uri.scheme != "http" && uri.scheme != "https") || uri.host.isNullOrBlank() || uri.userInfo != null || + !isOriginPath(uri) || uri.query != null || uri.fragment != null ) { @@ -177,6 +182,7 @@ tasks.withType().configureEach { dependencies { implementation("androidx.activity:activity:1.13.0") + implementation("androidx.webkit:webkit:1.16.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test:core:1.7.0") androidTestImplementation("androidx.test:runner:1.7.0") diff --git a/android/app/src/androidTest/java/org/whoneedhelp/mobile/AndroidClientInstrumentedTest.java b/android/app/src/androidTest/java/org/whoneedhelp/mobile/AndroidClientInstrumentedTest.java index 2d21a79..8863af5 100644 --- a/android/app/src/androidTest/java/org/whoneedhelp/mobile/AndroidClientInstrumentedTest.java +++ b/android/app/src/androidTest/java/org/whoneedhelp/mobile/AndroidClientInstrumentedTest.java @@ -11,11 +11,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.Manifest; +import android.app.Notification; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; +import android.os.SystemClock; +import android.service.notification.StatusBarNotification; import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ApplicationProvider; @@ -70,16 +74,20 @@ public final class AndroidClientInstrumentedTest { assertFalse(hasLocationPermission()); try (ActivityScenario scenario = launch("/permission-boundary")) { - scenario.onActivity(activity -> - activity.startForegroundService( - TrackingService.startIntent( - activity, - UUID.randomUUID().toString(), - "csrf-without-permission", - "_session=without-permission" - ) - ) - ); + scenario.onActivity(activity -> { + Intent intent = TrackingService.startIntent( + activity, + UUID.randomUUID().toString(), + "csrf-without-permission", + "_session=without-permission" + ); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + activity.startForegroundService(intent); + } else { + activity.startService(intent); + } + }); waitForServiceState(false); assertFalse( @@ -99,17 +107,45 @@ public final class AndroidClientInstrumentedTest { .withElement(findElement(Locator.ID, "location")) .perform(webClick()); - UiObject2 allow = device.wait( - Until.findObject( - By.res( - LOCATION_PERMISSION_CONTROLLER, - "permission_allow_foreground_only_button" - ) - ), - UI_TIMEOUT_MS - ); + UiObject2 allow = null; - if (allow == null) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + allow = device.wait( + Until.findObject( + By.res( + "com.android.packageinstaller", + "permission_allow_button" + ) + ), + UI_TIMEOUT_MS + ); + } + + if (allow == null && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + allow = device.wait( + Until.findObject( + By.res( + "com.google.android.packageinstaller", + "permission_allow_button" + ) + ), + UI_TIMEOUT_MS + ); + } + + if (allow == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + allow = device.wait( + Until.findObject( + By.res( + LOCATION_PERMISSION_CONTROLLER, + "permission_allow_foreground_only_button" + ) + ), + UI_TIMEOUT_MS + ); + } + + if (allow == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { allow = device.wait( Until.findObject(By.textContains("While using the app")), UI_TIMEOUT_MS @@ -328,16 +364,20 @@ public final class AndroidClientInstrumentedTest { ActivityScenario scenario, String assignmentId ) { - scenario.onActivity(activity -> - activity.startForegroundService( - TrackingService.startIntent( - activity, - assignmentId, - "instrumentation-csrf", - "_session=instrumentation" - ) - ) - ); + scenario.onActivity(activity -> { + Intent intent = TrackingService.startIntent( + activity, + assignmentId, + "instrumentation-csrf", + "_session=instrumentation" + ); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + activity.startForegroundService(intent); + } else { + activity.startService(intent); + } + }); } private void openNotificationAndClickStop() { @@ -347,14 +387,59 @@ public final class AndroidClientInstrumentedTest { UI_TIMEOUT_MS ); assertNotNull("Foreground tracking notification was not visible", active); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + sendStopActionFromActiveNotification(); + return; + } + UiObject2 stop = device.wait( Until.findObject(By.text(context.getString(R.string.tracking_stop_action))), UI_TIMEOUT_MS ); + assertNotNull("Foreground tracking notification had no Stop action", stop); stop.click(); } + private void sendStopActionFromActiveNotification() { + NotificationManager manager = context.getSystemService(NotificationManager.class); + String expectedTitle = context.getString(R.string.tracking_stop_action); + long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(UI_TIMEOUT_MS); + + while (System.nanoTime() < deadline) { + for (StatusBarNotification statusBarNotification : manager.getActiveNotifications()) { + Notification.Action[] actions = statusBarNotification.getNotification().actions; + + if (actions == null) { + continue; + } + + for (Notification.Action action : actions) { + if ( + action.title != null + && expectedTitle.contentEquals(action.title) + && action.actionIntent != null + ) { + try { + action.actionIntent.send(); + return; + } catch (PendingIntent.CanceledException error) { + throw new AssertionError( + "Foreground tracking Stop action was canceled", + error + ); + } + } + } + } + + SystemClock.sleep(50); + } + + throw new AssertionError("Foreground tracking notification had no Stop action"); + } + private boolean hasLocationPermission() { return context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED diff --git a/android/app/src/androidTest/java/org/whoneedhelp/mobile/TrackingProcessDeathProbeTest.java b/android/app/src/androidTest/java/org/whoneedhelp/mobile/TrackingProcessDeathProbeTest.java index 69303f5..8ccc4f5 100644 --- a/android/app/src/androidTest/java/org/whoneedhelp/mobile/TrackingProcessDeathProbeTest.java +++ b/android/app/src/androidTest/java/org/whoneedhelp/mobile/TrackingProcessDeathProbeTest.java @@ -39,15 +39,19 @@ public final class TrackingProcessDeathProbeTest { ); } - context.startForegroundService( - TrackingService.startIntent( - context, - UUID.randomUUID().toString(), - "process-death-csrf", - "_session=process-death" - ) + Intent intent = TrackingService.startIntent( + context, + UUID.randomUUID().toString(), + "process-death-csrf", + "_session=process-death" ); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } + // The host harness kills this exact process after observing the active // service and notification. Reaching the deadline means the harness did // not perform the required process-death probe. diff --git a/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java b/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java index fd0272d..f991133 100644 --- a/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java +++ b/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java @@ -15,7 +15,6 @@ import android.util.Log; import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; -import android.webkit.JavascriptInterface; import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; @@ -30,10 +29,17 @@ import androidx.activity.ComponentActivity; import androidx.activity.OnBackPressedCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.webkit.WebMessageCompat; +import androidx.webkit.WebViewCompat; +import androidx.webkit.WebViewFeature; import java.util.ArrayList; +import java.util.Collections; import java.util.UUID; +import org.json.JSONException; +import org.json.JSONObject; + public final class MainActivity extends ComponentActivity { private static final String LOG_TAG = "WhoNeedHelpWebView"; private WebView webView; @@ -106,7 +112,7 @@ public final class MainActivity extends ComponentActivity { cookieManager.setAcceptCookie(true); cookieManager.setAcceptThirdPartyCookies(webView, false); - webView.addJavascriptInterface(new NativeTrackingBridge(), "WhoNeedHelpAndroid"); + configureNativeTrackingBridge(); webView.setWebViewClient(new TrustedWebViewClient()); webView.setWebChromeClient(new LocationWebChromeClient()); getOnBackPressedDispatcher().addCallback( @@ -192,7 +198,6 @@ public final class MainActivity extends ComponentActivity { if (webView != null) { webView.stopLoading(); - webView.removeJavascriptInterface("WhoNeedHelpAndroid"); webView.setWebChromeClient(null); webView.setWebViewClient(null); webView.destroy(); @@ -275,6 +280,60 @@ public final class MainActivity extends ComponentActivity { nativeTrackingPermissionLauncher.launch(permissions.toArray(new String[0])); } + private void configureNativeTrackingBridge() { + if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { + return; + } + + WebViewCompat.addWebMessageListener( + webView, + "WhoNeedHelpAndroid", + Collections.singleton(trustedOrigin.originRule()), + ( + WebView view, + WebMessageCompat message, + Uri sourceOrigin, + boolean isMainFrame, + androidx.webkit.JavaScriptReplyProxy replyProxy + ) -> { + if ( + !isMainFrame + || sourceOrigin == null + || !trustedOrigin.matchesOrigin(sourceOrigin.toString()) + ) { + return; + } + + handleNativeTrackingMessage(message.getData()); + } + ); + } + + private void handleNativeTrackingMessage(String payload) { + if (payload == null) { + dispatchNativeTrackingError(); + return; + } + + try { + JSONObject message = new JSONObject(payload); + String action = message.optString("action", ""); + + if ("start".equals(action)) { + prepareNativeTracking( + message.optString("assignment_id", ""), + message.optString("csrf_token", "") + ); + } else if ("stop".equals(action)) { + stopService(new Intent(MainActivity.this, TrackingService.class)); + } else { + dispatchNativeTrackingError(); + } + } catch (JSONException exception) { + dispatchNativeTrackingError(); + } + } + private void startPendingNativeTracking() { PendingNativeTracking pending = pendingNativeTracking; pendingNativeTracking = null; @@ -519,20 +578,6 @@ public final class MainActivity extends ComponentActivity { } } - private final class NativeTrackingBridge { - @JavascriptInterface - public void startTracking(String assignmentId, String csrfToken) { - runOnUiThread(() -> prepareNativeTracking(assignmentId, csrfToken)); - } - - @JavascriptInterface - public void stopTracking() { - runOnUiThread(() -> - stopService(new Intent(MainActivity.this, TrackingService.class)) - ); - } - } - private static final class PendingNativeTracking { private final String assignmentId; private final String csrfToken; diff --git a/android/app/src/main/java/org/whoneedhelp/mobile/TrustedOrigin.java b/android/app/src/main/java/org/whoneedhelp/mobile/TrustedOrigin.java index 6a7e5a3..7f3fb97 100644 --- a/android/app/src/main/java/org/whoneedhelp/mobile/TrustedOrigin.java +++ b/android/app/src/main/java/org/whoneedhelp/mobile/TrustedOrigin.java @@ -30,6 +30,7 @@ final class TrustedOrigin { uri.getHost() == null || uri.getHost().trim().isEmpty() || uri.getUserInfo() != null || + !pathIsOrigin(uri.getPath()) || uri.getQuery() != null || uri.getFragment() != null ) { @@ -43,6 +44,22 @@ final class TrustedOrigin { return base.toString(); } + String originRule() { + try { + return new URI( + normalized(base.getScheme()), + null, + base.getHost(), + base.getPort(), + null, + null, + null + ).toString(); + } catch (URISyntaxException exception) { + throw new IllegalStateException("Trusted origin could not be normalized", exception); + } + } + boolean matches(String value) { try { URI candidate = new URI(value); diff --git a/android/app/src/test/java/org/whoneedhelp/mobile/TrustedOriginTest.java b/android/app/src/test/java/org/whoneedhelp/mobile/TrustedOriginTest.java index 401b473..7f2014a 100644 --- a/android/app/src/test/java/org/whoneedhelp/mobile/TrustedOriginTest.java +++ b/android/app/src/test/java/org/whoneedhelp/mobile/TrustedOriginTest.java @@ -1,6 +1,7 @@ package org.whoneedhelp.mobile; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -16,6 +17,7 @@ public final class TrustedOriginTest { assertFalse(origin.matches("https://help.example.evil.test/")); assertFalse(origin.matches("http://help.example/")); assertFalse(origin.matches("https://help.example:444/")); + assertEquals("https://help.example", origin.originRule()); } @Test @@ -25,6 +27,7 @@ public final class TrustedOriginTest { assertTrue(origin.matches("http://10.0.2.2:4010/requests")); assertTrue(origin.matchesOrigin("http://10.0.2.2:4010/")); assertFalse(origin.matchesOrigin("http://10.0.2.2:4010/requests")); + assertEquals("http://10.0.2.2:4010", origin.originRule()); } @Test @@ -41,5 +44,9 @@ public final class TrustedOriginTest { IllegalArgumentException.class, () -> TrustedOrigin.parse("https://help.example?redirect=evil", false) ); + assertThrows( + IllegalArgumentException.class, + () -> TrustedOrigin.parse("https://help.example/app", false) + ); } } diff --git a/assets/js/app.js b/assets/js/app.js index 76761e9..402b6bb 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -56,11 +56,46 @@ matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { }) const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") + +const activateProtectedTokenFragment = () => { + if (!window.location.hash.startsWith("#token=")) return + + const token = new URLSearchParams(window.location.hash.slice(1)).get("token") + window.history.replaceState(null, "", `${window.location.pathname}${window.location.search}`) + + if (!token || !/^[A-Za-z0-9_-]{43}$/.test(token)) return + + const candidates = [ + { + form: document.getElementById("magic-link-fragment-form"), + input: document.getElementById("magic-link-fragment-token"), + options: document.getElementById("standard-login-options") + }, + { + form: document.getElementById("email-change-fragment-form"), + input: document.getElementById("email-change-fragment-token") + } + ] + + const candidate = candidates.find(({form, input}) => + form instanceof HTMLFormElement && input instanceof HTMLInputElement + ) + + if (!candidate) return + + candidate.input.value = token + candidate.form.hidden = false + if (candidate.options instanceof HTMLElement) candidate.options.hidden = true + candidate.form.querySelector("button")?.focus() +} + +activateProtectedTokenFragment() + const liveSocket = new LiveSocket("/live", Socket, { longPollFallbackMs: 2500, params: { _csrf_token: csrfToken, - client_type: window.WhoNeedHelpAndroid ? "android" : "browser" + client_type: typeof window.WhoNeedHelpAndroid?.postMessage === "function" ? "android" : "browser" }, hooks: {...colocatedHooks, ...Hooks}, }) @@ -74,10 +109,14 @@ window.addEventListener("phx:reset-message-form", ({detail}) => { if (form instanceof HTMLFormElement) form.reset() }) window.addEventListener("phx:native-tracking-start", ({detail}) => { - window.WhoNeedHelpAndroid?.startTracking(detail.assignment_id, csrfToken) + window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({ + action: "start", + assignment_id: detail.assignment_id, + csrf_token: csrfToken + })) }) window.addEventListener("phx:native-tracking-stop", () => { - window.WhoNeedHelpAndroid?.stopTracking() + window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({action: "stop"})) }) // connect if there are any LiveViews on the page diff --git a/assets/js/hooks.js b/assets/js/hooks.js index b6b0b04..4c01860 100644 --- a/assets/js/hooks.js +++ b/assets/js/hooks.js @@ -1,5 +1,7 @@ import maplibregl from "maplibre-gl" +const trackingMinTimeMs = 5000 + const defaultStyle = { version: 8, sources: { @@ -134,8 +136,43 @@ export const Hooks = { LiveTracking: { mounted() { this.destroying = false + this.lastLocationSentAt = 0 + this.pendingLocation = null + this.locationTimer = undefined - if (window.WhoNeedHelpAndroid) { + this.sendLocation = payload => { + this.lastLocationSentAt = Date.now() + this.pendingLocation = null + this.pushEvent("location-update", payload) + } + + this.queueLocation = payload => { + const elapsed = Date.now() - this.lastLocationSentAt + + if (this.lastLocationSentAt === 0 || elapsed >= trackingMinTimeMs) { + if (this.locationTimer !== undefined) { + window.clearTimeout(this.locationTimer) + this.locationTimer = undefined + } + + this.sendLocation(payload) + return + } + + this.pendingLocation = payload + + if (this.locationTimer === undefined) { + this.locationTimer = window.setTimeout(() => { + this.locationTimer = undefined + + if (!this.destroying && this.pendingLocation) { + this.sendLocation(this.pendingLocation) + } + }, trackingMinTimeMs - elapsed) + } + } + + if (typeof window.WhoNeedHelpAndroid?.postMessage === "function") { this.nativeError = () => { this.pushEvent("location-error", {}) this.pushEvent("stop-tracking", {}) @@ -145,7 +182,7 @@ export const Hooks = { } this.watchId = navigator.geolocation.watchPosition( - position => this.pushEvent("location-update", { + position => this.queueLocation({ latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy_meters: position.coords.accuracy @@ -158,6 +195,13 @@ export const Hooks = { }, destroyed() { this.destroying = true + this.pendingLocation = null + + if (this.locationTimer !== undefined) { + window.clearTimeout(this.locationTimer) + this.locationTimer = undefined + } + if (this.nativeError) { window.removeEventListener("wnh:native-tracking-error", this.nativeError) } diff --git a/compose.external-boundaries.yaml b/compose.external-boundaries.yaml index 10f6a2f..cdfef33 100644 --- a/compose.external-boundaries.yaml +++ b/compose.external-boundaries.yaml @@ -13,6 +13,7 @@ x-boundary-app-environment: &boundary-app-environment GITHUB_OAUTH_AUTHORIZE_URL: http://external-mock:8080/oauth/authorize GITHUB_OAUTH_TOKEN_URL: http://external-mock:8080/oauth/token GITHUB_OAUTH_USER_URL: http://external-mock:8080/oauth/user + ALLOW_INSECURE_EXTERNAL_HTTP: "true" GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS: "100" GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS: "100" SMTP_RELAY: external-mock diff --git a/config/config.exs b/config/config.exs index ffffd2a..c9d24ce 100644 --- a/config/config.exs +++ b/config/config.exs @@ -30,6 +30,7 @@ config :who_need_help, app_role: :web, codex_session_id: "not-configured", e2e_routes: false, + secure_cookies: false, rate_limit_policies: %{}, map_tile_url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png" @@ -37,6 +38,9 @@ config :who_need_help, WhoNeedHelp.Repo, types: WhoNeedHelp.PostgrexTypes config :geo_postgis, json_library: Jason +config :phoenix, + filter_parameters: ["password", "token", "secret", "authorization", "cookie", "code"] + config :who_need_help, Oban, repo: WhoNeedHelp.Repo, queues: [maintenance: 2, push: 1], diff --git a/config/prod.exs b/config/prod.exs index b63761b..3c75a6a 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -19,6 +19,7 @@ e2e_routes = end config :who_need_help, :e2e_routes, e2e_routes +config :who_need_help, :secure_cookies, true config :who_need_help, WhoNeedHelpWeb.Endpoint, force_ssl: [ diff --git a/config/runtime.exs b/config/runtime.exs index 7cfc673..cd2aa31 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -8,6 +8,42 @@ app_role = other -> raise "APP_ROLE must be web, worker, or migrate; got #{inspect(other)}" end +rate_limit_policies = + case System.get_env("RATE_LIMIT_POLICIES_JSON") do + value when value in [nil, ""] -> + %{} + + json -> + case Jason.decode(json) do + {:ok, policies} when is_map(policies) -> + invalid_action = + Enum.find_value(policies, fn + {action, %{"limit" => limit, "window_seconds" => window}} + when is_binary(action) and action != "" and is_integer(limit) and limit > 0 and + is_integer(window) and window > 0 -> + nil + + {action, _policy} -> + if is_binary(action) and action != "", do: action, else: "" + end) + + if invalid_action do + raise """ + RATE_LIMIT_POLICIES_JSON policy #{inspect(invalid_action)} must contain positive \ + integer limit and window_seconds values. + """ + end + + policies + + {:ok, _other} -> + raise "RATE_LIMIT_POLICIES_JSON must be a JSON object." + + {:error, _reason} -> + raise "RATE_LIMIT_POLICIES_JSON must contain valid JSON." + end + end + config :who_need_help, app_role: app_role, codex_session_id: System.get_env("CODEX_SESSION_ID", "not-configured"), @@ -16,12 +52,7 @@ config :who_need_help, "MAP_TILE_URL", Application.fetch_env!(:who_need_help, :map_tile_url) ), - rate_limit_policies: - (case System.get_env("RATE_LIMIT_POLICIES_JSON") do - nil -> %{} - "" -> %{} - json -> Jason.decode!(json) - end) + rate_limit_policies: rate_limit_policies optional_positive_integer = fn name -> case System.get_env(name) do @@ -74,6 +105,13 @@ required_non_negative_integer = fn name -> end end +allow_insecure_external_http = + case System.get_env("ALLOW_INSECURE_EXTERNAL_HTTP", "false") do + "true" -> true + "false" -> false + other -> raise "ALLOW_INSECURE_EXTERNAL_HTTP must be true or false; got #{inspect(other)}" + end + oauth_endpoint = fn name, default -> value = case System.get_env(name) do @@ -83,11 +121,15 @@ oauth_endpoint = fn name, default -> case URI.parse(value) do %URI{scheme: scheme, host: host} - when scheme in ["http", "https"] and is_binary(host) and host != "" -> + when is_binary(host) and host != "" and + (scheme == "https" or (scheme == "http" and allow_insecure_external_http)) -> value _other -> - raise "#{name} must be an absolute HTTP or HTTPS URL." + raise """ + #{name} must be an absolute HTTPS URL. Plain HTTP is allowed only in the \ + isolated external-boundary drill with ALLOW_INSECURE_EXTERNAL_HTTP=true. + """ end end @@ -155,11 +197,15 @@ push_configuration = bearer_token != "" -> case URI.parse(endpoint) do %URI{scheme: scheme, host: host} - when scheme in ["http", "https"] and is_binary(host) and host != "" -> + when is_binary(host) and host != "" and + (scheme == "https" or (scheme == "http" and allow_insecure_external_http)) -> :ok _other -> - raise "PUSH_HTTP_ENDPOINT must be an absolute HTTP or HTTPS URL." + raise """ + PUSH_HTTP_ENDPOINT must be an absolute HTTPS URL. Plain HTTP is allowed only \ + in the isolated external-boundary drill with ALLOW_INSECURE_EXTERNAL_HTTP=true. + """ end [ diff --git a/docs/architecture.md b/docs/architecture.md index e6de257..28f4708 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -37,11 +37,13 @@ The Android module is a thin same-origin WebView shell with one native foreground location service. Its debug origin is supplied at build time from the repository's ignored `.env`; release builds require an explicit HTTPS origin. Authentication cookies, LiveView WebSockets, MapLibre, and private chat -use the same Phoenix application as the browser. A JavaScript bridge starts the -native service only from the visible Activity; the service posts the current -point through CSRF-protected, participant-authorized same-origin routes and -shows a persistent notification with Stop. Production signing and distribution -are separate operational work and are not represented as complete. +use the same Phoenix application as the browser. An origin-restricted +`WebViewCompat` message listener accepts tracking commands only from the trusted +main frame and starts the native service only from the visible Activity; the +service posts the current point through CSRF-protected, +participant-authorized same-origin routes and shows a persistent notification +with Stop. Production signing and distribution are separate operational work +and are not represented as complete. ## Application boundaries diff --git a/docs/operations.md b/docs/operations.md index f92a4a2..eaa71fd 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -96,6 +96,11 @@ network. It generates independent one-run OAuth and push credentials in an ignored mode-`0600` environment file, builds the production release plus a non-root standard-library Python protocol mock, and then verifies: +`compose.external-boundaries.yaml` is the only deployment file that sets +`ALLOW_INSECURE_EXTERNAL_HTTP=true`. Ordinary runtime configuration requires +HTTPS for OAuth and push endpoints so provider credentials are not sent over +plain HTTP. + 1. GitHub-compatible OAuth authorization, PKCE S256, token exchange, normalized user lookup, state mismatch, provider rejection, one-time-code replay, a fresh flow after a temporary token error, and a token timeout; diff --git a/e2e/tests/auth-settings.spec.ts b/e2e/tests/auth-settings.spec.ts index 6d60af2..0a31d10 100644 --- a/e2e/tests/auth-settings.spec.ts +++ b/e2e/tests/auth-settings.spec.ts @@ -47,12 +47,16 @@ test("registration, email confirmation, password, and email change work end to e const emailChangeLink = await waitForApplicationEmailLink( request, changedEmail, - "/users/settings/confirm-email/", + "/users/settings/confirm-email", ); recoverableOneTimeNavigations.add(emailChangeLink); try { await user.page.goto(emailChangeLink); + await expect(user.page.locator("#email-change-fragment-form")).toBeVisible(); + await user.page + .getByRole("button", { name: "Confirm email change" }) + .click(); await expect(user.page.getByText("Email changed successfully.")).toBeVisible(); } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/e2e/tests/helpers.ts b/e2e/tests/helpers.ts index 465f16b..d130bd5 100644 --- a/e2e/tests/helpers.ts +++ b/e2e/tests/helpers.ts @@ -300,7 +300,7 @@ async function waitForMagicLink( return waitForApplicationEmailLink( request, email, - "/users/log-in/", + "/users/log-in", previousMessageID, ); } @@ -325,7 +325,7 @@ export async function registerAndConfirm( await expect(page.getByText(`An email was sent to ${email}`)).toBeVisible(); await gotoWithTransientRetry(page, await waitForMagicLink(request, email)); - await expect(page.getByRole("heading", { name: `Welcome ${email}` })).toBeVisible(); + await expect(page.locator("#magic-link-fragment-form")).toBeVisible(); await page .getByRole("button", { name: "Confirm and log in only this time" }) .click(); diff --git a/lib/mix/tasks/wnh.staging_android_e2e.ex b/lib/mix/tasks/wnh.staging_android_e2e.ex index cf5ed8f..ae4b18d 100644 --- a/lib/mix/tasks/wnh.staging_android_e2e.ex +++ b/lib/mix/tasks/wnh.staging_android_e2e.ex @@ -151,7 +151,7 @@ defmodule Mix.Tasks.Wnh.StagingAndroidE2e do "path" => "/requests/#{fixture.request.id}" }, "assignment" => %{"id" => fixture.assignment.id}, - "helper_login_path" => "/users/log-in/#{fixture.helper_login_token}" + "helper_login_path" => "/users/log-in#token=#{fixture.helper_login_token}" } context.manifest_path |> Path.dirname() |> File.mkdir_p!() @@ -350,7 +350,7 @@ defmodule Mix.Tasks.Wnh.StagingAndroidE2e do unless manifest["schema_version"] == 1 and manifest["run_id"] == context.run_id and manifest["database"] == context.database and valid_users? and valid_request? and valid_assignment? and is_binary(manifest["helper_login_path"]) and - String.starts_with?(manifest["helper_login_path"], "/users/log-in/") do + String.starts_with?(manifest["helper_login_path"], "/users/log-in#token=") do Mix.raise("Android staging E2E manifest does not match the requested run and database") end end diff --git a/lib/who_need_help/accounts.ex b/lib/who_need_help/accounts.ex index f374e87..afcf016 100644 --- a/lib/who_need_help/accounts.ex +++ b/lib/who_need_help/accounts.ex @@ -61,7 +61,7 @@ defmodule WhoNeedHelp.Accounts do """ def get_user_by_email(email) when is_binary(email) do - Repo.get_by(User, email: email) + Repo.get_by(User, email: String.trim(email)) end @doc """ @@ -78,7 +78,7 @@ defmodule WhoNeedHelp.Accounts do """ def get_user_by_email_and_password(email, password) when is_binary(email) and is_binary(password) do - user = Repo.get_by(User, email: email) + user = get_user_by_email(email) if User.valid_password?(user, password) and user.moderation_status != :suspended, do: user @@ -381,9 +381,9 @@ defmodule WhoNeedHelp.Accounts do Repo.transact(fn -> with {:ok, query} <- UserToken.verify_change_email_token_query(token, context), - %UserToken{sent_to: email} <- Repo.one(query), + %UserToken{sent_to: email} <- query |> lock("FOR UPDATE") |> Repo.one(), {:ok, user} <- Repo.update(User.email_changeset(user, %{email: email})), - {_count, _result} <- + {count, _result} when count > 0 <- Repo.delete_all(from(UserToken, where: [user_id: ^user.id, context: ^context])) do {:ok, user} else @@ -480,34 +480,46 @@ defmodule WhoNeedHelp.Accounts do `mix help phx.gen.auth`. """ def login_user_by_magic_link(token) do - {:ok, query} = UserToken.verify_magic_link_token_query(token) + with {:ok, query} <- UserToken.verify_magic_link_token_query(token) do + result = + Repo.transact(fn -> + case query |> lock("FOR UPDATE") |> Repo.one() do + {%User{moderation_status: :suspended}, token} -> + with {:ok, _token} <- Repo.delete(token) do + {:ok, {:rejected, :not_found}} + end - case Repo.one(query) do - {%User{moderation_status: :suspended}, token} -> - Repo.delete!(token) - {:error, :not_found} + # Prevent session fixation attacks by disallowing magic links for unconfirmed users with password + {%User{confirmed_at: nil, hashed_password: hash}, _token} when not is_nil(hash) -> + raise """ + magic link log in is not allowed for unconfirmed users with a password set! - # Prevent session fixation attacks by disallowing magic links for unconfirmed users with password - {%User{confirmed_at: nil, hashed_password: hash}, _token} when not is_nil(hash) -> - raise """ - magic link log in is not allowed for unconfirmed users with a password set! + This cannot happen with the default implementation, which indicates that you + might have adapted the code to a different use case. Please make sure to read the + "Mixing magic link and password registration" section of `mix help phx.gen.auth`. + """ - This cannot happen with the default implementation, which indicates that you - might have adapted the code to a different use case. Please make sure to read the - "Mixing magic link and password registration" section of `mix help phx.gen.auth`. - """ + {%User{confirmed_at: nil} = user, _token} -> + user + |> User.confirm_changeset() + |> update_user_and_delete_all_tokens_in_transaction() - {%User{confirmed_at: nil} = user, _token} -> - user - |> User.confirm_changeset() - |> update_user_and_delete_all_tokens() + {user, token} -> + with {:ok, _token} <- Repo.delete(token) do + {:ok, {user, []}} + end - {user, token} -> - Repo.delete!(token) - {:ok, {user, []}} + nil -> + {:error, :not_found} + end + end) - nil -> - {:error, :not_found} + case result do + {:ok, {:rejected, reason}} -> {:error, reason} + other -> other + end + else + _invalid_token -> {:error, :not_found} end end @@ -516,7 +528,7 @@ defmodule WhoNeedHelp.Accounts do ## Examples - iex> deliver_user_update_email_instructions(user, current_email, &url(~p"/users/settings/confirm-email/#{&1}")) + iex> deliver_user_update_email_instructions(user, current_email, &"https://example.test/users/settings/confirm-email#token=#{&1}") {:ok, %{to: ..., body: ...}} """ @@ -546,6 +558,12 @@ defmodule WhoNeedHelp.Accounts do :ok end + def delete_expired_user_tokens(now \\ DateTime.utc_now(:second)) do + now + |> UserToken.expired_tokens_query() + |> Repo.delete_all() + end + ## Token helper defp before_moderation_user(query, nil), do: query @@ -567,14 +585,16 @@ defmodule WhoNeedHelp.Accounts do end defp update_user_and_delete_all_tokens(changeset) do - Repo.transact(fn -> - with {:ok, user} <- Repo.update(changeset) do - tokens_to_expire = Repo.all_by(UserToken, user_id: user.id) + Repo.transact(fn -> update_user_and_delete_all_tokens_in_transaction(changeset) end) + end - Repo.delete_all(from(t in UserToken, where: t.id in ^Enum.map(tokens_to_expire, & &1.id))) + defp update_user_and_delete_all_tokens_in_transaction(changeset) do + with {:ok, user} <- Repo.update(changeset) do + tokens_to_expire = Repo.all_by(UserToken, user_id: user.id) - {:ok, {user, tokens_to_expire}} - end - end) + Repo.delete_all(from(t in UserToken, where: t.id in ^Enum.map(tokens_to_expire, & &1.id))) + + {:ok, {user, tokens_to_expire}} + end end end diff --git a/lib/who_need_help/accounts/social_identity.ex b/lib/who_need_help/accounts/social_identity.ex index 21d0360..5c8f8b0 100644 --- a/lib/who_need_help/accounts/social_identity.ex +++ b/lib/who_need_help/accounts/social_identity.ex @@ -21,10 +21,10 @@ defmodule WhoNeedHelp.Accounts.SocialIdentity do identity |> cast(attrs, [:provider, :profile_url, :handle]) |> validate_required([:provider, :profile_url, :user_id]) - |> validate_format(:profile_url, ~r/^https?:\/\/[^\s]+$/i, - message: "must be a complete http(s) URL" + |> validate_format(:profile_url, ~r/^https:\/\/[^\s]+$/i, + message: "must be a complete HTTPS URL" ) - |> validate_length(:profile_url, max: 500) + |> validate_length(:profile_url, max: 255) |> validate_length(:handle, max: 100) |> unique_constraint([:provider, :provider_uid]) end @@ -51,7 +51,7 @@ defmodule WhoNeedHelp.Accounts.SocialIdentity do message: "must be a GitHub profile URL" ) |> validate_length(:provider_uid, max: 255) - |> validate_length(:profile_url, max: 500) + |> validate_length(:profile_url, max: 255) |> validate_length(:handle, max: 100) |> unique_constraint([:provider, :provider_uid]) end diff --git a/lib/who_need_help/accounts/user.ex b/lib/who_need_help/accounts/user.ex index 04da1ef..6569bf1 100644 --- a/lib/who_need_help/accounts/user.ex +++ b/lib/who_need_help/accounts/user.ex @@ -89,6 +89,7 @@ defmodule WhoNeedHelp.Accounts.User do |> validate_length(:display_name, min: 2, max: 80) |> validate_length(:bio, max: 600) |> validate_inclusion(:locale, ~w(en uk ru)) + |> validate_length(:tip_url, max: 255) |> validate_url(:tip_url) end @@ -108,12 +109,12 @@ defmodule WhoNeedHelp.Accounts.User do defp validate_url(changeset, field) do validate_change(changeset, field, fn ^field, value -> case URI.parse(value) do - %URI{scheme: scheme, host: host} - when scheme in ["https", "http"] and is_binary(host) and host != "" -> + %URI{scheme: "https", host: host} + when is_binary(host) and host != "" -> [] _ -> - [{field, "must be a full http(s) URL"}] + [{field, "must be a full HTTPS URL"}] end end) end diff --git a/lib/who_need_help/accounts/user_token.ex b/lib/who_need_help/accounts/user_token.ex index 9e13a3b..11845d2 100644 --- a/lib/who_need_help/accounts/user_token.ex +++ b/lib/who_need_help/accounts/user_token.ex @@ -153,6 +153,18 @@ defmodule WhoNeedHelp.Accounts.UserToken do end end + def expired_tokens_query(now \\ DateTime.utc_now(:second)) do + magic_link_cutoff = DateTime.add(now, -@magic_link_validity_in_minutes, :minute) + change_email_cutoff = DateTime.add(now, -@change_email_validity_in_days, :day) + session_cutoff = DateTime.add(now, -@session_validity_in_days, :day) + + from token in UserToken, + where: + (token.context == "login" and token.inserted_at <= ^magic_link_cutoff) or + (like(token.context, "change:%") and token.inserted_at <= ^change_email_cutoff) or + (token.context == "session" and token.inserted_at <= ^session_cutoff) + end + defp by_token_and_context_query(token, context) do from UserToken, where: [token: ^token, context: ^context] end diff --git a/lib/who_need_help/activities.ex b/lib/who_need_help/activities.ex index 1e5abc6..027088b 100644 --- a/lib/who_need_help/activities.ex +++ b/lib/who_need_help/activities.ex @@ -19,13 +19,15 @@ defmodule WhoNeedHelp.Activities do def subscribe, do: Phoenix.PubSub.subscribe(WhoNeedHelp.PubSub, @topic) + def subscribe_user(%Scope{user: user}), + do: Phoenix.PubSub.subscribe(WhoNeedHelp.PubSub, "activities:user:#{user.id}") + def subscribe_activity(id), do: Phoenix.PubSub.subscribe(WhoNeedHelp.PubSub, "activity:#{id}") def notify_activity_updated(id) do - activity = load_activity(id) - broadcast({:activity_updated, activity}) - broadcast_activity(id, {:activity_updated, activity}) + broadcast({:activity_updated, load_activity_summary(id)}) + broadcast_activity(id, {:activity_updated, load_activity(id)}) :ok end @@ -37,7 +39,7 @@ defmodule WhoNeedHelp.Activities do now = DateTime.utc_now(:second) limit = Pagination.limit(options) cursor = Pagination.cursor(options) - public_creator = Accounts.public_user_query(social_identities: true) + public_creator = Accounts.public_user_query() Activity |> where( @@ -231,39 +233,41 @@ defmodule WhoNeedHelp.Activities do end with {:ok, activity} <- result do - activity = load_activity(activity.id) - broadcast({:activity_created, activity}) - {:ok, activity} + broadcast({:activity_created, load_activity_summary(activity.id)}) + {:ok, load_activity(activity.id)} end end def request_to_join(%Scope{user: user} = scope, activity_id) do result = - with {:ok, _limit} <- Trust.authorize_action(scope, :join_activity) do + with {:ok, activity_id} <- cast_id(activity_id), + {:ok, _limit} <- Trust.authorize_action(scope, :join_activity) do Repo.transact(fn -> - activity = locked_activity(activity_id) - now = DateTime.utc_now(:second) + with %Activity{} = activity <- locked_activity(activity_id) do + now = DateTime.utc_now(:second) + :ok = Trust.lock_user_pair(activity.creator_id, user.id) - :ok = Trust.lock_user_pair(activity.creator_id, user.id) + cond do + activity.creator_id == user.id -> + {:error, :organizer_already_joined} - cond do - activity.creator_id == user.id -> - {:error, :organizer_already_joined} + activity.status != :open or not is_nil(activity.hidden_at) -> + {:error, :not_open} - activity.status != :open or not is_nil(activity.hidden_at) -> - {:error, :not_open} + DateTime.compare(activity.join_deadline, now) != :gt -> + {:error, :join_closed} - DateTime.compare(activity.join_deadline, now) != :gt -> - {:error, :join_closed} + Trust.blocked_between?(activity.creator_id, user.id) -> + {:error, :blocked} - Trust.blocked_between?(activity.creator_id, user.id) -> - {:error, :blocked} + approved_count(activity.id) >= activity.capacity -> + {:error, :capacity_reached} - approved_count(activity.id) >= activity.capacity -> - {:error, :capacity_reached} - - true -> - upsert_join_request(activity.id, user.id, now) + true -> + upsert_join_request(activity.id, user.id, now) + end + else + nil -> {:error, :not_found} end end) end @@ -275,44 +279,47 @@ defmodule WhoNeedHelp.Activities do result = with {:ok, participant_id} <- cast_id(participant_id) do Repo.transact(fn -> - participant = locked_participant(participant_id) - activity = locked_activity(participant.activity_id) - :ok = Trust.lock_user_pair(activity.creator_id, participant.user_id) + with %Participant{} = participant <- locked_participant(participant_id), + %Activity{} = activity <- locked_activity(participant.activity_id) do + :ok = Trust.lock_user_pair(activity.creator_id, participant.user_id) - cond do - activity.creator_id != organizer.id -> - {:error, :forbidden} + cond do + activity.creator_id != organizer.id -> + {:error, :forbidden} - participant.status != :requested -> - {:error, :invalid_transition} + participant.status != :requested -> + {:error, :invalid_transition} - activity.status != :open -> - {:error, :not_open} + activity.status != :open -> + {:error, :not_open} - Trust.blocked_between?(activity.creator_id, participant.user_id) -> - {:error, :blocked} + Trust.blocked_between?(activity.creator_id, participant.user_id) -> + {:error, :blocked} - approved_count(activity.id) >= activity.capacity -> - {:error, :capacity_reached} + approved_count(activity.id) >= activity.capacity -> + {:error, :capacity_reached} - true -> - with {:ok, participant} <- - participant - |> Participant.changeset(%{ - status: :approved, - reviewed_at: DateTime.utc_now(:second) - }) - |> Repo.update(), - {:ok, _audit} <- - Trust.audit( - organizer.id, - "activity.participant_approved", - "activity_participant", - participant.id, - %{"activity_id" => activity.id, "user_id" => participant.user_id} - ) do - {:ok, participant} - end + true -> + with {:ok, participant} <- + participant + |> Participant.changeset(%{ + status: :approved, + reviewed_at: DateTime.utc_now(:second) + }) + |> Repo.update(), + {:ok, _audit} <- + Trust.audit( + organizer.id, + "activity.participant_approved", + "activity_participant", + participant.id, + %{"activity_id" => activity.id, "user_id" => participant.user_id} + ) do + {:ok, participant} + end + end + else + nil -> {:error, :not_found} end end) end @@ -328,40 +335,42 @@ defmodule WhoNeedHelp.Activities do def leave_activity(%Scope{user: user}, activity_id) do result = - Repo.transact(fn -> - participant = - Participant - |> where( - [participant], - participant.activity_id == ^activity_id and participant.user_id == ^user.id - ) - |> lock("FOR UPDATE") - |> Repo.one() + with {:ok, activity_id} <- cast_id(activity_id) do + Repo.transact(fn -> + participant = + Participant + |> where( + [participant], + participant.activity_id == ^activity_id and participant.user_id == ^user.id + ) + |> lock("FOR UPDATE") + |> Repo.one() - cond do - is_nil(participant) -> - {:error, :not_found} + cond do + is_nil(participant) -> + {:error, :not_found} - participant.role == :organizer -> - {:error, :organizer_cannot_leave} + participant.role == :organizer -> + {:error, :organizer_cannot_leave} - participant.status not in [:requested, :approved] -> - {:error, :invalid_transition} + participant.status not in [:requested, :approved] -> + {:error, :invalid_transition} - true -> - with {:ok, participant} <- - participant - |> Participant.changeset(%{ - status: :left, - left_at: DateTime.utc_now(:second) - }) - |> Repo.update(), - {:ok, _audit} <- - Trust.audit(user.id, "activity.left", "activity", activity_id) do - {:ok, participant} - end - end - end) + true -> + with {:ok, participant} <- + participant + |> Participant.changeset(%{ + status: :left, + left_at: DateTime.utc_now(:second) + }) + |> Repo.update(), + {:ok, _audit} <- + Trust.audit(user.id, "activity.left", "activity", activity_id) do + {:ok, participant} + end + end + end) + end after_participant_change(result, activity_id, :participant_left) end @@ -376,29 +385,33 @@ defmodule WhoNeedHelp.Activities do def send_message(%Scope{user: user} = scope, activity_id, attrs) do result = - with {:ok, _limit} <- Trust.authorize_action(scope, :send_activity_message) do + with {:ok, activity_id} <- cast_id(activity_id), + {:ok, _limit} <- Trust.authorize_action(scope, :send_activity_message) do Repo.transact(fn -> - activity = locked_activity(activity_id) - :ok = Trust.lock_user_pair(activity.creator_id, user.id) + with %Activity{} = activity <- locked_activity(activity_id) do + :ok = Trust.lock_user_pair(activity.creator_id, user.id) - cond do - activity.status != :open or not is_nil(activity.hidden_at) -> - {:error, :not_open} + cond do + activity.status != :open or not is_nil(activity.hidden_at) -> + {:error, :not_open} - Trust.blocked_between?(activity.creator_id, user.id) -> - {:error, :blocked} + Trust.blocked_between?(activity.creator_id, user.id) -> + {:error, :blocked} - not approved_participant_id?(activity.id, user.id) -> - {:error, :forbidden} + not approved_participant_id?(activity.id, user.id) -> + {:error, :forbidden} - true -> - %Message{} - |> Message.changeset(%{ - body: attrs["body"] || attrs[:body], - activity_id: activity.id, - sender_id: user.id - }) - |> Repo.insert() + true -> + %Message{} + |> Message.changeset(%{ + body: attrs["body"] || attrs[:body], + activity_id: activity.id, + sender_id: user.id + }) + |> Repo.insert() + end + else + nil -> {:error, :not_found} end end) end @@ -514,34 +527,36 @@ defmodule WhoNeedHelp.Activities do defp transition_participant(organizer_id, participant_id, status) do result = Repo.transact(fn -> - participant = locked_participant(participant_id) - activity = locked_activity(participant.activity_id) + with %Participant{} = participant <- locked_participant(participant_id), + %Activity{} = activity <- locked_activity(participant.activity_id) do + cond do + activity.creator_id != organizer_id -> + {:error, :forbidden} - cond do - activity.creator_id != organizer_id -> - {:error, :forbidden} + participant.status != :requested -> + {:error, :invalid_transition} - participant.status != :requested -> - {:error, :invalid_transition} - - true -> - with {:ok, participant} <- - participant - |> Participant.changeset(%{ - status: status, - reviewed_at: DateTime.utc_now(:second) - }) - |> Repo.update(), - {:ok, _audit} <- - Trust.audit( - organizer_id, - "activity.participant_#{status}", - "activity_participant", - participant.id, - %{"activity_id" => activity.id, "user_id" => participant.user_id} - ) do - {:ok, participant} - end + true -> + with {:ok, participant} <- + participant + |> Participant.changeset(%{ + status: status, + reviewed_at: DateTime.utc_now(:second) + }) + |> Repo.update(), + {:ok, _audit} <- + Trust.audit( + organizer_id, + "activity.participant_#{status}", + "activity_participant", + participant.id, + %{"activity_id" => activity.id, "user_id" => participant.user_id} + ) do + {:ok, participant} + end + end + else + nil -> {:error, :not_found} end end) @@ -550,37 +565,42 @@ defmodule WhoNeedHelp.Activities do defp transition_activity(user_id, activity_id, target_status) do result = - Repo.transact(fn -> - activity = locked_activity(activity_id) + with {:ok, activity_id} <- cast_id(activity_id) do + Repo.transact(fn -> + with %Activity{} = activity <- locked_activity(activity_id) do + cond do + activity.creator_id != user_id -> + {:error, :forbidden} - cond do - activity.creator_id != user_id -> - {:error, :forbidden} + activity.status != :open -> + {:error, :invalid_transition} - activity.status != :open -> - {:error, :invalid_transition} + true -> + timestamp = DateTime.utc_now(:second) - true -> - timestamp = DateTime.utc_now(:second) + attrs = + case target_status do + :cancelled -> %{status: :cancelled, cancelled_at: timestamp} + :completed -> %{status: :completed, completed_at: timestamp} + end - attrs = - case target_status do - :cancelled -> %{status: :cancelled, cancelled_at: timestamp} - :completed -> %{status: :completed, completed_at: timestamp} - end - - with {:ok, activity} <- activity |> Ecto.Changeset.change(attrs) |> Repo.update(), - {:ok, _audit} <- - Trust.audit( - user_id, - "activity.#{target_status}", - "activity", - activity.id - ) do - {:ok, activity} + with {:ok, activity} <- + activity |> Ecto.Changeset.change(attrs) |> Repo.update(), + {:ok, _audit} <- + Trust.audit( + user_id, + "activity.#{target_status}", + "activity", + activity.id + ) do + {:ok, activity} + end end - end - end) + else + nil -> {:error, :not_found} + end + end) + end with {:ok, activity} <- result do activity = load_activity(activity.id) @@ -592,9 +612,10 @@ defmodule WhoNeedHelp.Activities do defp after_participant_change({:ok, participant}, activity_id, event) when is_binary(activity_id) do - activity = load_activity(activity_id) - broadcast({:activity_updated, activity}) - broadcast_activity(activity.id, {event, participant}) + summary = load_activity_summary(activity_id) + broadcast({:activity_updated, summary}) + broadcast_user(participant.user_id, {:my_activity_updated, summary, member?(participant)}) + broadcast_activity(activity_id, {event, participant}) {:ok, participant} end @@ -619,6 +640,16 @@ defmodule WhoNeedHelp.Activities do |> preload_activity() end + defp load_activity_summary(id) do + public_creator = Accounts.public_user_query() + + Activity + |> where([activity], activity.id == ^id) + |> with_approved_participant_count() + |> preload([activity], category: :parent, creator: ^public_creator) + |> Repo.one!() + end + defp get_loaded_activity(id) do Activity |> Repo.get(id) @@ -682,20 +713,24 @@ defmodule WhoNeedHelp.Activities do Activity |> where([activity], activity.id == ^id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() end defp locked_participant(id) do Participant |> where([participant], participant.id == ^id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() end defp maybe_filter_category(query, value) when value in [nil, ""], do: query - defp maybe_filter_category(query, value), - do: where(query, [activity], activity.category_id == ^value) + defp maybe_filter_category(query, value) do + case Ecto.UUID.cast(value) do + {:ok, category_id} -> where(query, [activity], activity.category_id == ^category_id) + :error -> where(query, [activity], false) + end + end defp cast_id(value) do case Ecto.UUID.cast(value) do @@ -706,6 +741,11 @@ defmodule WhoNeedHelp.Activities do defp broadcast(message), do: Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, @topic, message) + defp broadcast_user(user_id, message), + do: Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, "activities:user:#{user_id}", message) + defp broadcast_activity(id, message), do: Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, "activity:#{id}", message) + + defp member?(%Participant{status: status}), do: status in [:requested, :approved] end diff --git a/lib/who_need_help/activities/activity.ex b/lib/who_need_help/activities/activity.ex index a74b550..bba1847 100644 --- a/lib/who_need_help/activities/activity.ex +++ b/lib/who_need_help/activities/activity.ex @@ -63,7 +63,11 @@ defmodule WhoNeedHelp.Activities.Activity do ]) |> validate_length(:title, min: 5, max: 120) |> validate_length(:description, min: 10, max: 2_000) - |> validate_number(:capacity, greater_than_or_equal_to: 2) + |> validate_length(:location_label, max: 255) + |> validate_number(:capacity, + greater_than_or_equal_to: 2, + less_than_or_equal_to: 2_147_483_647 + ) |> validate_acceptance(:safety_confirmed, message: "confirm the safety guidance before publishing" ) @@ -93,8 +97,8 @@ defmodule WhoNeedHelp.Activities.Activity do latitude = attrs["latitude"] || attrs[:latitude] longitude = attrs["longitude"] || attrs[:longitude] - with {lat, ""} <- Float.parse(to_string(latitude)), - {lng, ""} <- Float.parse(to_string(longitude)), + with {:ok, lat} <- parse_coordinate(latitude), + {:ok, lng} <- parse_coordinate(longitude), true <- lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do put_change(changeset, :location, %Geo.Point{coordinates: {lng, lat}, srid: 4326}) else @@ -102,6 +106,18 @@ defmodule WhoNeedHelp.Activities.Activity do end end + defp parse_coordinate(value) when is_float(value), do: {:ok, value} + defp parse_coordinate(value) when is_integer(value), do: {:ok, value * 1.0} + + defp parse_coordinate(value) when is_binary(value) do + case Float.parse(value) do + {coordinate, ""} -> {:ok, coordinate} + _ -> :error + end + end + + defp parse_coordinate(_value), do: :error + defp validate_schedule(changeset) do starts_at = get_field(changeset, :starts_at) join_deadline = get_field(changeset, :join_deadline) diff --git a/lib/who_need_help/catalog.ex b/lib/who_need_help/catalog.ex index ff317ab..dd4f4b1 100644 --- a/lib/who_need_help/catalog.ex +++ b/lib/who_need_help/catalog.ex @@ -303,8 +303,8 @@ defmodule WhoNeedHelp.Catalog do defp valid_structured_value?(%{"type" => "select", "options" => options}, value) when is_list(options) do Enum.any?(options, fn - %{"value" => allowed} -> to_string(allowed) == to_string(value) - allowed -> to_string(allowed) == to_string(value) + %{"value" => allowed} -> same_scalar_value?(allowed, value) + allowed -> same_scalar_value?(allowed, value) end) end @@ -320,6 +320,13 @@ defmodule WhoNeedHelp.Catalog do defp valid_structured_value?(_field, _value), do: false + defp same_scalar_value?(left, right) + when (is_binary(left) or is_atom(left) or is_number(left) or is_boolean(left)) and + (is_binary(right) or is_atom(right) or is_number(right) or is_boolean(right)), + do: to_string(left) == to_string(right) + + defp same_scalar_value?(_left, _right), do: false + defp normalize_structured_value(%{"type" => "boolean"}, "true"), do: true defp normalize_structured_value(%{"type" => "boolean"}, "false"), do: false defp normalize_structured_value(_field, value), do: value diff --git a/lib/who_need_help/catalog/category.ex b/lib/who_need_help/catalog/category.ex index ea81f0c..0fce1ce 100644 --- a/lib/who_need_help/catalog/category.ex +++ b/lib/who_need_help/catalog/category.ex @@ -36,6 +36,10 @@ defmodule WhoNeedHelp.Catalog.Category do |> validate_required([:slug, :names]) |> validate_format(:slug, ~r/^[a-z0-9-]+$/) |> validate_length(:slug, max: 80) + |> validate_number(:sort_order, + greater_than_or_equal_to: -2_147_483_648, + less_than_or_equal_to: 2_147_483_647 + ) |> validate_names() |> validate_descriptions() |> validate_structured_fields() diff --git a/lib/who_need_help/help.ex b/lib/who_need_help/help.ex index e1a799e..ad58e7b 100644 --- a/lib/who_need_help/help.ex +++ b/lib/who_need_help/help.ex @@ -200,62 +200,69 @@ defmodule WhoNeedHelp.Help do end def accept_request(%Scope{user: helper}, request_id) do - now = DateTime.utc_now(:second) - code = handover_code(request_id) - result = - with {:ok, _limit} <- Trust.authorize_action(Scope.for_user(helper), :accept_request) do + with {:ok, request_id} <- cast_id(request_id), + {:ok, _limit} <- Trust.authorize_action(Scope.for_user(helper), :accept_request) do + now = DateTime.utc_now(:second) + code = handover_code(request_id) + Repo.transact(fn -> request = HelpRequest |> where([r], r.id == ^request_id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() - :ok = Trust.lock_user_pair(request.requester_id, helper.id) + if request do + :ok = Trust.lock_user_pair(request.requester_id, helper.id) - cond do - request.requester_id == helper.id -> - {:error, :own_request} + cond do + request.requester_id == helper.id -> + {:error, :own_request} - not is_nil(request.hidden_at) -> - {:error, :not_open} + not is_nil(request.hidden_at) -> + {:error, :not_open} - Trust.blocked_between?(request.requester_id, helper.id) -> - {:error, :blocked} + Trust.blocked_between?(request.requester_id, helper.id) -> + {:error, :blocked} - request.status != :open -> - {:error, :not_open} + request.status != :open -> + {:error, :not_open} - DateTime.compare(request.expires_at, now) != :gt -> - {:error, :expired} + DateTime.compare(request.expires_at, now) != :gt -> + {:error, :expired} - true -> - with {:ok, assignment} <- - %Assignment{} - |> Assignment.changeset(%{ - request_id: request.id, - helper_id: helper.id, - accepted_at: now, - handover_code_hash: code_hash(code) - }) - |> Repo.insert(), - {:ok, _request} <- - request |> Ecto.Changeset.change(status: :matched) |> Repo.update(), - {:ok, _audit} <- - Trust.audit(helper.id, "request.accepted", "assignment", assignment.id, %{ - "request_id" => request.id - }), - {:ok, _push_job} <- - Push.enqueue_request_accepted( - assignment.id, - request.id, - request.requester_id - ) do - {:ok, assignment} - end + true -> + with {:ok, assignment} <- + %Assignment{} + |> Assignment.changeset(%{ + request_id: request.id, + helper_id: helper.id, + accepted_at: now, + handover_code_hash: code_hash(code) + }) + |> Repo.insert(), + {:ok, _request} <- + request |> Ecto.Changeset.change(status: :matched) |> Repo.update(), + {:ok, _audit} <- + Trust.audit(helper.id, "request.accepted", "assignment", assignment.id, %{ + "request_id" => request.id + }), + {:ok, _push_job} <- + Push.enqueue_request_accepted( + assignment.id, + request.id, + request.requester_id + ) do + {:ok, assignment} + end + end + else + {:error, :not_found} end end) + else + :error -> {:error, :not_found} end case result do @@ -283,70 +290,86 @@ defmodule WhoNeedHelp.Help do end def verify_handover(%Scope{user: user}, assignment_id, code) do - with {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :verify_handover) do + with {:ok, assignment_id} <- cast_id(assignment_id), + true <- valid_handover_code?(code), + {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :verify_handover) do Repo.transact(fn -> - assignment = locked_assignment(assignment_id) - request = Repo.get!(HelpRequest, assignment.request_id) - :ok = Trust.lock_user_pair(assignment.helper_id, request.requester_id) + with %Assignment{} = assignment <- locked_assignment(assignment_id) do + request = Repo.get!(HelpRequest, assignment.request_id) + :ok = Trust.lock_user_pair(assignment.helper_id, request.requester_id) - cond do - user.id != assignment.helper_id -> - {:error, :forbidden} + cond do + user.id != assignment.helper_id -> + {:error, :forbidden} - Trust.blocked_between?(assignment.helper_id, request.requester_id) -> - {:error, :blocked} + Trust.blocked_between?(assignment.helper_id, request.requester_id) -> + {:error, :blocked} - assignment.status not in [:accepted, :in_progress] or - not is_nil(assignment.handover_verified_at) -> - {:error, :invalid_transition} + assignment.status not in [:accepted, :in_progress] or + not is_nil(assignment.handover_verified_at) -> + {:error, :invalid_transition} - not Plug.Crypto.secure_compare(code_hash(code), assignment.handover_code_hash) -> - {:error, :invalid_code} + not Plug.Crypto.secure_compare(code_hash(code), assignment.handover_code_hash) -> + {:error, :invalid_code} - true -> - now = DateTime.utc_now(:second) + true -> + now = DateTime.utc_now(:second) - assignment - |> Assignment.changeset(%{handover_verified_at: now}) - |> maybe_complete(request) - |> audit_assignment_transition(user.id, "handover.verified", request.id) + assignment + |> Assignment.changeset(%{handover_verified_at: now}) + |> maybe_complete(request) + |> audit_assignment_transition(user.id, "handover.verified", request.id) + end + else + nil -> {:error, :not_found} end end) + else + :error -> {:error, :not_found} + false -> {:error, :invalid_code} end |> after_transition() end def cancel_request(%Scope{user: user}, request_id) do - with {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :cancel_request) do + with {:ok, request_id} <- cast_id(request_id), + {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :cancel_request) do Repo.transact(fn -> request = HelpRequest |> where([r], r.id == ^request_id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() - if request.requester_id == user.id and request.status in [:open, :matched] do - now = DateTime.utc_now(:second) + cond do + is_nil(request) -> + {:error, :not_found} - assignment = - Assignment - |> where([assignment], assignment.request_id == ^request.id) - |> lock("FOR UPDATE") - |> Repo.one() + request.requester_id == user.id and request.status in [:open, :matched] -> + now = DateTime.utc_now(:second) - with {:ok, request} <- - request - |> Ecto.Changeset.change(status: :cancelled, cancelled_at: now) - |> Repo.update(), - {:ok, _assignment} <- cancel_assignment(assignment), - {:ok, _audit} <- - Trust.audit(user.id, "request.cancelled", "request", request.id) do - {:ok, request} - end - else - {:error, :forbidden} + assignment = + Assignment + |> where([assignment], assignment.request_id == ^request.id) + |> lock("FOR UPDATE") + |> Repo.one() + + with {:ok, request} <- + request + |> Ecto.Changeset.change(status: :cancelled, cancelled_at: now) + |> Repo.update(), + {:ok, _assignment} <- cancel_assignment(assignment), + {:ok, _audit} <- + Trust.audit(user.id, "request.cancelled", "request", request.id) do + {:ok, request} + end + + true -> + {:error, :forbidden} end end) + else + :error -> {:error, :not_found} end |> case do {:ok, request} -> @@ -361,32 +384,39 @@ defmodule WhoNeedHelp.Help do end def withdraw_assignment(%Scope{user: user}, assignment_id) do - with {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :withdraw_assignment) do + with {:ok, assignment_id} <- cast_id(assignment_id), + {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), :withdraw_assignment) do Repo.transact(fn -> - assignment = locked_assignment(assignment_id) - request = Repo.get!(HelpRequest, assignment.request_id) + with %Assignment{} = assignment <- locked_assignment(assignment_id) do + request = Repo.get!(HelpRequest, assignment.request_id) - if assignment.helper_id == user.id and assignment.status in [:accepted, :in_progress] do - now = DateTime.utc_now(:second) + if assignment.helper_id == user.id and + assignment.status in [:accepted, :in_progress] do + now = DateTime.utc_now(:second) - with {:ok, assignment} <- - assignment - |> Assignment.changeset(%{status: :cancelled}) - |> Repo.update(), - {:ok, _request} <- - request - |> Ecto.Changeset.change(status: :cancelled, cancelled_at: now) - |> Repo.update(), - {:ok, _audit} <- - Trust.audit(user.id, "assignment.withdrawn", "assignment", assignment.id, %{ - "request_id" => request.id - }) do - {:ok, assignment} + with {:ok, assignment} <- + assignment + |> Assignment.changeset(%{status: :cancelled}) + |> Repo.update(), + {:ok, _request} <- + request + |> Ecto.Changeset.change(status: :cancelled, cancelled_at: now) + |> Repo.update(), + {:ok, _audit} <- + Trust.audit(user.id, "assignment.withdrawn", "assignment", assignment.id, %{ + "request_id" => request.id + }) do + {:ok, assignment} + end + else + {:error, :invalid_transition} end else - {:error, :invalid_transition} + nil -> {:error, :not_found} end end) + else + :error -> {:error, :not_found} end |> after_transition() |> case do @@ -469,8 +499,8 @@ defmodule WhoNeedHelp.Help do request.assignment.status in [:accepted, :in_progress] and not Trust.blocked_between?(scope.user.id, request.requester_id) - if (owner or matched_participant) and - request.location_visibility in [:hidden, :exact_for_active_match] do + if owner or + (matched_participant and request.location_visibility == :exact_for_active_match) do %Geo.Point{coordinates: {lng, lat}} = request.location %{latitude: lat, longitude: lng, exact: true} else @@ -490,53 +520,63 @@ defmodule WhoNeedHelp.Help do end defp transition_assignment(user, assignment_id, action) do - with {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), action) do + with {:ok, assignment_id} <- cast_id(assignment_id), + {:ok, _limit} <- Trust.authorize_action(Scope.for_user(user), action) do Repo.transact(fn -> - assignment = locked_assignment(assignment_id) - request = Repo.get!(HelpRequest, assignment.request_id) - :ok = Trust.lock_user_pair(assignment.helper_id, request.requester_id) - now = DateTime.utc_now(:second) + with %Assignment{} = assignment <- locked_assignment(assignment_id) do + request = Repo.get!(HelpRequest, assignment.request_id) + :ok = Trust.lock_user_pair(assignment.helper_id, request.requester_id) + now = DateTime.utc_now(:second) - if Trust.blocked_between?(assignment.helper_id, request.requester_id) do - {:error, :blocked} - else - case {action, assignment.status, user.id} do - {:start, :accepted, helper_id} when helper_id == assignment.helper_id -> - with {:ok, assignment} <- - assignment - |> Assignment.changeset(%{status: :in_progress, started_at: now}) - |> Repo.update(), - {:ok, _} <- - request |> Ecto.Changeset.change(status: :in_progress) |> Repo.update(), - {:ok, _audit} <- - Trust.audit(user.id, "assignment.started", "assignment", assignment.id, %{ - "request_id" => request.id - }) do - {:ok, assignment} - end - - {:confirm, status, user_id} when status in [:accepted, :in_progress] -> - attrs = - cond do - user_id == assignment.helper_id -> %{helper_confirmed_at: now} - user_id == request.requester_id -> %{requester_confirmed_at: now} - true -> nil + if Trust.blocked_between?(assignment.helper_id, request.requester_id) do + {:error, :blocked} + else + case {action, assignment.status, user.id} do + {:start, :accepted, helper_id} when helper_id == assignment.helper_id -> + with {:ok, assignment} <- + assignment + |> Assignment.changeset(%{status: :in_progress, started_at: now}) + |> Repo.update(), + {:ok, _} <- + request |> Ecto.Changeset.change(status: :in_progress) |> Repo.update(), + {:ok, _audit} <- + Trust.audit( + user.id, + "assignment.started", + "assignment", + assignment.id, + %{"request_id" => request.id} + ) do + {:ok, assignment} end - if attrs do - assignment - |> Assignment.changeset(attrs) - |> maybe_complete(request) - |> audit_assignment_transition(user.id, "assignment.confirmed", request.id) - else - {:error, :forbidden} - end + {:confirm, status, user_id} when status in [:accepted, :in_progress] -> + attrs = + cond do + user_id == assignment.helper_id -> %{helper_confirmed_at: now} + user_id == request.requester_id -> %{requester_confirmed_at: now} + true -> nil + end - _ -> - {:error, :invalid_transition} + if attrs do + assignment + |> Assignment.changeset(attrs) + |> maybe_complete(request) + |> audit_assignment_transition(user.id, "assignment.confirmed", request.id) + else + {:error, :forbidden} + end + + _ -> + {:error, :invalid_transition} + end end + else + nil -> {:error, :not_found} end end) + else + :error -> {:error, :not_found} end |> after_transition() end @@ -571,10 +611,15 @@ defmodule WhoNeedHelp.Help do Assignment |> where([a], a.id == ^id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() end - defp code_hash(code), do: :crypto.hash(:sha256, to_string(code)) + defp valid_handover_code?(code) when is_binary(code), + do: Regex.match?(~r/^\d{6}$/, code) + + defp valid_handover_code?(_code), do: false + + defp code_hash(code) when is_binary(code), do: :crypto.hash(:sha256, code) defp after_transition({:ok, assignment}) do request = get_request!(assignment.request_id) @@ -648,8 +693,31 @@ defmodule WhoNeedHelp.Help do defp maybe_filter(query, _field, value) when value in [nil, ""], do: query - defp maybe_filter(query, field, value), - do: where(query, [request], field(request, ^field) == ^value) + defp maybe_filter(query, :category_id, value) do + case Ecto.UUID.cast(value) do + {:ok, category_id} -> where(query, [request], request.category_id == ^category_id) + :error -> where(query, [request], false) + end + end + + defp maybe_filter(query, :urgency, value) do + case normalize_urgency(value) do + {:ok, urgency} -> where(query, [request], request.urgency == ^urgency) + :error -> where(query, [request], false) + end + end + + defp normalize_urgency(value) when value in [:now, "now"], do: {:ok, :now} + defp normalize_urgency(value) when value in [:today, "today"], do: {:ok, :today} + defp normalize_urgency(value) when value in [:scheduled, "scheduled"], do: {:ok, :scheduled} + defp normalize_urgency(_value), do: :error + + defp cast_id(id) do + case Ecto.UUID.cast(id) do + {:ok, id} -> {:ok, id} + :error -> :error + end + end defp validate_structured_data(changeset) do category_id = Ecto.Changeset.get_field(changeset, :category_id) diff --git a/lib/who_need_help/help/help_request.ex b/lib/who_need_help/help/help_request.ex index 1dc3c36..84b1de0 100644 --- a/lib/who_need_help/help/help_request.ex +++ b/lib/who_need_help/help/help_request.ex @@ -64,6 +64,7 @@ defmodule WhoNeedHelp.Help.HelpRequest do |> validate_length(:title, min: 5, max: 120) |> validate_length(:description, min: 10, max: 2_000) |> validate_length(:pickup_instructions, max: 1_000) + |> validate_length(:location_label, max: 255) |> validate_acceptance(:safety_confirmed, message: "confirm the safety guidance before publishing" ) @@ -80,8 +81,8 @@ defmodule WhoNeedHelp.Help.HelpRequest do lat = attrs["latitude"] || attrs[:latitude] lng = attrs["longitude"] || attrs[:longitude] - with {lat, ""} <- Float.parse(to_string(lat)), - {lng, ""} <- Float.parse(to_string(lng)), + with {:ok, lat} <- parse_coordinate(lat), + {:ok, lng} <- parse_coordinate(lng), true <- lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do put_change(changeset, :location, %Geo.Point{coordinates: {lng, lat}, srid: 4326}) else @@ -89,6 +90,18 @@ defmodule WhoNeedHelp.Help.HelpRequest do end end + defp parse_coordinate(value) when is_float(value), do: {:ok, value} + defp parse_coordinate(value) when is_integer(value), do: {:ok, value * 1.0} + + defp parse_coordinate(value) when is_binary(value) do + case Float.parse(value) do + {coordinate, ""} -> {:ok, coordinate} + _ -> :error + end + end + + defp parse_coordinate(_value), do: :error + defp validate_expiry(changeset) do validate_change(changeset, :expires_at, fn :expires_at, value -> if DateTime.after?(value, DateTime.utc_now()), diff --git a/lib/who_need_help/messaging.ex b/lib/who_need_help/messaging.ex index 815736e..42af3b2 100644 --- a/lib/who_need_help/messaging.ex +++ b/lib/who_need_help/messaging.ex @@ -53,31 +53,35 @@ defmodule WhoNeedHelp.Messaging do Assignment |> where([current], current.id == ^assignment.id) |> lock("FOR UPDATE") - |> Repo.one!() - |> Repo.preload(:request) + |> Repo.one() - request = current.request - recipient_id = counterpart_id(user.id, current, request) + if current do + current = Repo.preload(current, :request) + request = current.request + recipient_id = counterpart_id(user.id, current, request) - with true <- Help.participant?(scope, current), - :ok <- Trust.lock_user_pair(user.id, recipient_id), - false <- Trust.blocked_between?(user.id, recipient_id), - {:ok, message} <- - %Message{assignment_id: current.id, sender_id: user.id} - |> Message.changeset(attrs) - |> Repo.insert(), - {:ok, _push_job} <- - Push.enqueue_message_created( - message.id, - current.id, - request.id, - recipient_id - ) do - {:ok, message} + with true <- Help.participant?(scope, current), + :ok <- Trust.lock_user_pair(user.id, recipient_id), + false <- Trust.blocked_between?(user.id, recipient_id), + {:ok, message} <- + %Message{assignment_id: current.id, sender_id: user.id} + |> Message.changeset(attrs) + |> Repo.insert(), + {:ok, _push_job} <- + Push.enqueue_message_created( + message.id, + current.id, + request.id, + recipient_id + ) do + {:ok, message} + else + true -> {:error, :blocked} + false -> {:error, :forbidden} + other -> other + end else - true -> {:error, :blocked} - false -> {:error, :forbidden} - other -> other + {:error, :not_found} end end) diff --git a/lib/who_need_help/tracking/position.ex b/lib/who_need_help/tracking/position.ex index 897525e..aba8c9e 100644 --- a/lib/who_need_help/tracking/position.ex +++ b/lib/who_need_help/tracking/position.ex @@ -26,12 +26,24 @@ defmodule WhoNeedHelp.Tracking.Position do lat = attrs["latitude"] || attrs[:latitude] lng = attrs["longitude"] || attrs[:longitude] - with {lat, ""} <- Float.parse(to_string(lat)), - {lng, ""} <- Float.parse(to_string(lng)), + with {:ok, lat} <- parse_coordinate(lat), + {:ok, lng} <- parse_coordinate(lng), true <- lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do put_change(changeset, :position, %Geo.Point{coordinates: {lng, lat}, srid: 4326}) else _ -> add_error(changeset, :position, "is invalid") end end + + defp parse_coordinate(value) when is_float(value), do: {:ok, value} + defp parse_coordinate(value) when is_integer(value), do: {:ok, value * 1.0} + + defp parse_coordinate(value) when is_binary(value) do + case Float.parse(value) do + {coordinate, ""} -> {:ok, coordinate} + _ -> :error + end + end + + defp parse_coordinate(_value), do: :error end diff --git a/lib/who_need_help/trust.ex b/lib/who_need_help/trust.ex index 8ad59ee..795389e 100644 --- a/lib/who_need_help/trust.ex +++ b/lib/who_need_help/trust.ex @@ -53,47 +53,51 @@ defmodule WhoNeedHelp.Trust do Assignment |> where([current], current.id == ^assignment.id) |> lock("FOR UPDATE") - |> Repo.one!() + |> Repo.one() - request = Repo.get!(HelpRequest, current.request_id) + if current do + request = Repo.get!(HelpRequest, current.request_id) - cond do - current.status != :completed -> - {:error, :forbidden} + cond do + current.status != :completed -> + {:error, :forbidden} - user.id not in [current.helper_id, request.requester_id] -> - {:error, :forbidden} + user.id not in [current.helper_id, request.requester_id] -> + {:error, :forbidden} - true -> - reviewee_id = - if user.id == current.helper_id, - do: request.requester_id, - else: current.helper_id + true -> + reviewee_id = + if user.id == current.helper_id, + do: request.requester_id, + else: current.helper_id - with {:ok, review} <- - %Review{} - |> Review.changeset( - Map.merge(attrs, %{ - "assignment_id" => current.id, - "reviewer_id" => user.id, - "reviewee_id" => reviewee_id - }) - ) - |> Repo.insert(), - reviews <- Repo.all(from r in Review, where: r.assignment_id == ^current.id), - true <- length(reviews) <= 2, - revealed? <- length(reviews) == 2, - {_count, _rows} <- - maybe_reveal_reviews(current.id, revealed?), - {:ok, _audit} <- - audit(user.id, "review.submitted", "assignment", current.id, %{ - "revealed" => revealed? - }) do - {:ok, review} - else - false -> {:error, :invalid_review_count} - other -> other - end + with {:ok, review} <- + %Review{} + |> Review.changeset( + Map.merge(attrs, %{ + "assignment_id" => current.id, + "reviewer_id" => user.id, + "reviewee_id" => reviewee_id + }) + ) + |> Repo.insert(), + reviews <- Repo.all(from r in Review, where: r.assignment_id == ^current.id), + true <- length(reviews) <= 2, + revealed? <- length(reviews) == 2, + {_count, _rows} <- + maybe_reveal_reviews(current.id, revealed?), + {:ok, _audit} <- + audit(user.id, "review.submitted", "assignment", current.id, %{ + "revealed" => revealed? + }) do + {:ok, review} + else + false -> {:error, :invalid_review_count} + other -> other + end + end + else + {:error, :not_found} end end) end @@ -870,7 +874,7 @@ defmodule WhoNeedHelp.Trust do defp users_by_id([]), do: %{} defp users_by_id(ids) do - User + Accounts.public_user_query() |> where([user], user.id in ^ids) |> Repo.all() |> Map.new(&{&1.id, &1}) @@ -1059,51 +1063,59 @@ defmodule WhoNeedHelp.Trust do defp authorize_report_target(%Scope{user: user}, %{"request_id" => request_id}) when is_binary(request_id) do - case Repo.get(HelpRequest, request_id) do - %HelpRequest{requester_id: requester_id} when requester_id != user.id -> :ok - %HelpRequest{} -> {:error, :cannot_report_self} - nil -> {:error, :not_found} + with {:ok, request_id} <- cast_id(request_id) do + case Repo.get(HelpRequest, request_id) do + %HelpRequest{requester_id: requester_id} when requester_id != user.id -> :ok + %HelpRequest{} -> {:error, :cannot_report_self} + nil -> {:error, :not_found} + end end end defp authorize_report_target(scope, %{"assignment_id" => assignment_id}) when is_binary(assignment_id) do - case Repo.get(Assignment, assignment_id) do - %Assignment{} = assignment -> - if Help.participant?(scope, assignment), do: :ok, else: {:error, :forbidden} + with {:ok, assignment_id} <- cast_id(assignment_id) do + case Repo.get(Assignment, assignment_id) do + %Assignment{} = assignment -> + if Help.participant?(scope, assignment), do: :ok, else: {:error, :forbidden} - nil -> - {:error, :not_found} + nil -> + {:error, :not_found} + end end end defp authorize_report_target(scope, %{"message_id" => message_id}) when is_binary(message_id) do - case Message |> Repo.get(message_id) |> Repo.preload(:assignment) do - %Message{sender_id: sender_id, assignment: assignment} -> - cond do - sender_id == scope.user.id -> {:error, :cannot_report_self} - Help.participant?(scope, assignment) -> :ok - true -> {:error, :forbidden} - end + with {:ok, message_id} <- cast_id(message_id) do + case Message |> Repo.get(message_id) |> Repo.preload(:assignment) do + %Message{sender_id: sender_id, assignment: assignment} -> + cond do + sender_id == scope.user.id -> {:error, :cannot_report_self} + Help.participant?(scope, assignment) -> :ok + true -> {:error, :forbidden} + end - nil -> - {:error, :not_found} + nil -> + {:error, :not_found} + end end end defp authorize_report_target(%Scope{user: user}, %{"activity_id" => activity_id}) when is_binary(activity_id) do - case Repo.get(Activity, activity_id) do - %Activity{creator_id: creator_id} when creator_id == user.id -> - {:error, :cannot_report_self} + with {:ok, activity_id} <- cast_id(activity_id) do + case Repo.get(Activity, activity_id) do + %Activity{creator_id: creator_id} when creator_id == user.id -> + {:error, :cannot_report_self} - %Activity{} = activity -> - if is_nil(activity.hidden_at) or activity_participant?(activity.id, user.id), - do: :ok, - else: {:error, :not_found} + %Activity{} = activity -> + if is_nil(activity.hidden_at) or activity_participant?(activity.id, user.id), + do: :ok, + else: {:error, :not_found} - nil -> - {:error, :not_found} + nil -> + {:error, :not_found} + end end end @@ -1112,17 +1124,19 @@ defmodule WhoNeedHelp.Trust do %{"activity_message_id" => message_id} ) when is_binary(message_id) do - case Repo.get(ActivityMessage, message_id) do - %ActivityMessage{sender_id: sender_id} when sender_id == user.id -> - {:error, :cannot_report_self} + with {:ok, message_id} <- cast_id(message_id) do + case Repo.get(ActivityMessage, message_id) do + %ActivityMessage{sender_id: sender_id} when sender_id == user.id -> + {:error, :cannot_report_self} - %ActivityMessage{activity_id: activity_id} -> - if activity_participant?(activity_id, user.id), - do: :ok, - else: {:error, :forbidden} + %ActivityMessage{activity_id: activity_id} -> + if activity_participant?(activity_id, user.id), + do: :ok, + else: {:error, :forbidden} - nil -> - {:error, :not_found} + nil -> + {:error, :not_found} + end end end @@ -1195,18 +1209,16 @@ defmodule WhoNeedHelp.Trust do ) |> where( [assignment, request], - (assignment.helper_id == ^first_user_id and request.requester_id == ^second_user_id) or - (assignment.helper_id == ^second_user_id and request.requester_id == ^first_user_id) + assignment.status in [:accepted, :in_progress] and + ((assignment.helper_id == ^first_user_id and + request.requester_id == ^second_user_id) or + (assignment.helper_id == ^second_user_id and + request.requester_id == ^first_user_id)) ) - |> select([assignment, request], {assignment.id, request.id, assignment.status}) + |> select([assignment, request], {assignment.id, request.id}) |> Repo.all() - active_assignment_ids = - assignments - |> Enum.filter(fn {_assignment_id, _request_id, status} -> - status in [:accepted, :in_progress] - end) - |> Enum.map(&elem(&1, 0)) + active_assignment_ids = Enum.map(assignments, &elem(&1, 0)) active_sessions = if active_assignment_ids == [] do diff --git a/lib/who_need_help/trust/rate_limiter.ex b/lib/who_need_help/trust/rate_limiter.ex index 34958bf..137f769 100644 --- a/lib/who_need_help/trust/rate_limiter.ex +++ b/lib/who_need_help/trust/rate_limiter.ex @@ -30,7 +30,11 @@ defmodule WhoNeedHelp.Trust.RateLimiter do defp policy(action) do policies = Application.get_env(:who_need_help, :rate_limit_policies, %{}) - raw = Map.get(policies, action) || Map.get(policies, String.to_existing_atom(action)) + + raw = + if is_map(policies) do + Map.get(policies, action) || Map.get(policies, String.to_existing_atom(action)) + end case raw do %{limit: limit, window_seconds: window} diff --git a/lib/who_need_help/workers/expire_requests.ex b/lib/who_need_help/workers/expire_requests.ex index 81b0a0d..a0a4e7a 100644 --- a/lib/who_need_help/workers/expire_requests.ex +++ b/lib/who_need_help/workers/expire_requests.ex @@ -16,13 +16,15 @@ defmodule WhoNeedHelp.Workers.ExpireRequests do with {:ok, expired} <- expire_available(now, 0), {:ok, cleanup} <- Tracking.cleanup_finished_sessions() do {pruned_buckets, _} = RateLimiter.prune_expired() + {pruned_user_tokens, _} = WhoNeedHelp.Accounts.delete_expired_user_tokens(now) {:ok, %{ expired: expired, tracking_positions_deleted: cleanup.positions_deleted, tracking_sessions_ended: cleanup.sessions_ended, - rate_limit_buckets_pruned: pruned_buckets + rate_limit_buckets_pruned: pruned_buckets, + user_tokens_pruned: pruned_user_tokens }} end end diff --git a/lib/who_need_help_web/components/core_components.ex b/lib/who_need_help_web/components/core_components.ex index 2eff0b0..e59f131 100644 --- a/lib/who_need_help_web/components/core_components.ex +++ b/lib/who_need_help_web/components/core_components.ex @@ -195,7 +195,7 @@ defmodule WhoNeedHelpWeb.CoreComponents do |> assign(field: nil, id: assigns.id || field.id) |> assign(:errors, Enum.map(errors, &translate_error(&1))) |> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end) - |> assign_new(:value, fn -> field.value end) + |> assign_new(:value, fn -> safe_form_value(field.value) end) |> input() end @@ -289,7 +289,7 @@ defmodule WhoNeedHelpWeb.CoreComponents do type={@type} name={@name} id={@id} - value={Phoenix.HTML.Form.normalize_value(@type, @value)} + value={normalized_input_value(@type, @value)} class={[ @class || "w-full input", @errors != [] && (@error_class || "input-error") @@ -302,6 +302,24 @@ defmodule WhoNeedHelpWeb.CoreComponents do """ end + defp normalized_input_value("password", _value), do: nil + + defp normalized_input_value(type, value), + do: Phoenix.HTML.Form.normalize_value(type, value) + + defp safe_form_value(value) when is_list(value) do + if Enum.all?(value, &safe_form_value?/1), + do: value, + else: nil + end + + defp safe_form_value(value) do + if Phoenix.HTML.Safe.impl_for(value), do: value, else: nil + end + + defp safe_form_value?(value) when is_list(value), do: Enum.all?(value, &safe_form_value?/1) + defp safe_form_value?(value), do: not is_nil(Phoenix.HTML.Safe.impl_for(value)) + # Helper used by inputs to generate form errors defp error(assigns) do ~H""" diff --git a/lib/who_need_help_web/controllers/metrics_controller.ex b/lib/who_need_help_web/controllers/metrics_controller.ex index 6bf417b..a5458c0 100644 --- a/lib/who_need_help_web/controllers/metrics_controller.ex +++ b/lib/who_need_help_web/controllers/metrics_controller.ex @@ -16,6 +16,7 @@ defmodule WhoNeedHelpWeb.MetricsController do conn |> put_resp_header("cache-control", "no-store") + |> put_resp_header("x-wnh-node", to_string(node())) |> put_resp_content_type(@content_type) |> send_resp(:ok, body) else diff --git a/lib/who_need_help_web/controllers/user_registration_controller.ex b/lib/who_need_help_web/controllers/user_registration_controller.ex index 1be7121..67ec477 100644 --- a/lib/who_need_help_web/controllers/user_registration_controller.ex +++ b/lib/who_need_help_web/controllers/user_registration_controller.ex @@ -1,40 +1,44 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do use WhoNeedHelpWeb, :controller + require Logger + alias WhoNeedHelp.Accounts alias WhoNeedHelp.Accounts.User alias WhoNeedHelp.Trust.RateLimiter + plug :put_no_store + def new(conn, _params) do changeset = Accounts.change_user_registration(%User{}, %{}, validate_unique: false) render(conn, :new, changeset: changeset) end - def create(conn, %{"user" => user_params}) do - email_scope = user_params["email"] |> to_string() |> String.trim() |> String.downcase() + def create(conn, %{"user" => user_params}) when is_map(user_params) do + user_params = normalize_registration_params(user_params) + email_scope = normalize_email_scope(user_params["email"]) with {:ok, _limit} <- RateLimiter.check(:registration_email, email_scope), result <- Accounts.register_user(user_params) do case result do {:ok, user} -> - {:ok, _} = - Accounts.deliver_login_instructions( - user, - &url(~p"/users/log-in/#{&1}") - ) - - conn - |> put_flash( - :info, - gettext( - "An email was sent to %{email}, please access it to confirm your account.", - email: user.email - ) - ) - |> redirect(to: ~p"/users/log-in") + deliver_registration_instructions(conn, user) + registration_response(conn) {:error, %Ecto.Changeset{} = changeset} -> - render(conn, :new, changeset: changeset) + if unique_email_error?(changeset) do + case Accounts.get_user_by_email(user_params["email"]) do + %User{moderation_status: status} = user when status != :suspended -> + deliver_registration_instructions(conn, user) + + _missing_or_suspended -> + :ok + end + + registration_response(conn) + else + render(conn, :new, changeset: changeset) + end end else {:error, :rate_limited} -> @@ -49,4 +53,76 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do ) end end + + def create(conn, _params) do + conn + |> put_status(:bad_request) + |> put_flash(:error, gettext("The registration form is invalid.")) + |> render(:new, + changeset: Accounts.change_user_registration(%User{}, %{}, validate_unique: false) + ) + end + + defp normalize_email_scope(email) when is_binary(email), + do: email |> String.trim() |> String.downcase() + + defp normalize_email_scope(_email), do: "" + + defp normalize_registration_params(params) do + params + |> normalize_binary_param("email") + |> normalize_binary_param("display_name") + |> normalize_binary_param("locale") + |> normalize_boolean_param("terms_accepted") + end + + defp normalize_binary_param(params, key) do + case Map.fetch(params, key) do + {:ok, value} when is_binary(value) -> params + {:ok, _invalid} -> Map.put(params, key, "") + :error -> params + end + end + + defp normalize_boolean_param(params, key) do + case Map.fetch(params, key) do + {:ok, value} when is_binary(value) or is_boolean(value) -> params + {:ok, _invalid} -> Map.put(params, key, false) + :error -> params + end + end + + defp deliver_registration_instructions(conn, user) do + case Accounts.deliver_login_instructions( + user, + &"#{url(conn, ~p"/users/log-in")}#token=#{URI.encode_www_form(&1)}" + ) do + {:ok, _email} -> :ok + {:error, _reason} -> Logger.warning("Unable to send registration login instructions") + end + end + + defp registration_response(conn) do + conn + |> put_flash( + :info, + gettext( + "If this address can be registered or signed in, login instructions will arrive shortly." + ) + ) + |> redirect(to: ~p"/users/log-in") + end + + defp unique_email_error?(%Ecto.Changeset{} = changeset) do + Enum.any?(changeset.errors, fn + {:email, {_message, options}} -> + Keyword.get(options, :constraint) == :unique or + Keyword.get(options, :validation) == :unsafe_unique + + _other -> + false + end) + end + + defp put_no_store(conn, _opts), do: put_resp_header(conn, "cache-control", "no-store") end diff --git a/lib/who_need_help_web/controllers/user_session_controller.ex b/lib/who_need_help_web/controllers/user_session_controller.ex index 8708a32..ae41ce4 100644 --- a/lib/who_need_help_web/controllers/user_session_controller.ex +++ b/lib/who_need_help_web/controllers/user_session_controller.ex @@ -1,10 +1,15 @@ defmodule WhoNeedHelpWeb.UserSessionController do use WhoNeedHelpWeb, :controller + require Logger + alias WhoNeedHelp.Accounts alias WhoNeedHelp.Trust.RateLimiter alias WhoNeedHelpWeb.UserAuth + plug :assign_magic_link_form + plug :put_no_store + def new(conn, _params) do email = get_in(conn.assigns, [:current_scope, Access.key(:user), Access.key(:email)]) form = Phoenix.Component.to_form(%{"email" => email}, as: "user") @@ -13,7 +18,8 @@ defmodule WhoNeedHelpWeb.UserSessionController do end # magic link login - def create(conn, %{"user" => %{"token" => token} = user_params} = params) do + def create(conn, %{"user" => %{"token" => token} = user_params} = params) + when is_binary(token) do info = case params do %{"_action" => "confirmed"} -> gettext("User confirmed successfully.") @@ -34,7 +40,8 @@ defmodule WhoNeedHelpWeb.UserSessionController do end # email + password login - def create(conn, %{"user" => %{"email" => email, "password" => password} = user_params}) do + def create(conn, %{"user" => %{"email" => email, "password" => password} = user_params}) + when is_binary(email) and is_binary(password) do email_scope = email |> String.trim() |> String.downcase() case RateLimiter.check(:password_login_email, email_scope) do @@ -56,17 +63,23 @@ defmodule WhoNeedHelpWeb.UserSessionController do end # magic link request - def create(conn, %{"user" => %{"email" => email}}) do + def create(conn, %{"user" => %{"email" => email}}) when is_binary(email) do email_scope = email |> String.trim() |> String.downcase() case RateLimiter.check(:magic_link_email, email_scope) do {:ok, _limit} -> case Accounts.get_user_by_email(email) do %Accounts.User{moderation_status: status} = user when status != :suspended -> - Accounts.deliver_login_instructions( - user, - &url(~p"/users/log-in/#{&1}") - ) + case Accounts.deliver_login_instructions( + user, + &"#{url(~p"/users/log-in")}#token=#{URI.encode_www_form(&1)}" + ) do + {:ok, _email} -> + :ok + + {:error, _reason} -> + Logger.warning("Unable to send requested login instructions") + end _missing_or_suspended -> :ok @@ -92,19 +105,11 @@ defmodule WhoNeedHelpWeb.UserSessionController do end end - def confirm(conn, %{"token" => token}) do - if user = Accounts.get_user_by_magic_link_token(token) do - form = Phoenix.Component.to_form(%{"token" => token}, as: "user") - - conn - |> assign(:user, user) - |> assign(:form, form) - |> render(:confirm) - else - conn - |> put_flash(:error, gettext("Magic link is invalid or it has expired.")) - |> redirect(to: ~p"/users/log-in") - end + def create(conn, _params) do + conn + |> put_status(:bad_request) + |> put_flash(:error, gettext("The sign-in form is invalid.")) + |> render(:new, form: Phoenix.Component.to_form(%{}, as: "user")) end def delete(conn, _params) do @@ -119,4 +124,14 @@ defmodule WhoNeedHelpWeb.UserSessionController do |> put_flash(:error, gettext("Invalid email or password")) |> render(:new, form: Phoenix.Component.to_form(user_params, as: "user")) end + + defp assign_magic_link_form(conn, _opts) do + assign( + conn, + :magic_link_form, + Phoenix.Component.to_form(%{"token" => ""}, as: "user") + ) + end + + defp put_no_store(conn, _opts), do: put_resp_header(conn, "cache-control", "no-store") end diff --git a/lib/who_need_help_web/controllers/user_session_html/confirm.html.heex b/lib/who_need_help_web/controllers/user_session_html/confirm.html.heex deleted file mode 100644 index 5220ee3..0000000 --- a/lib/who_need_help_web/controllers/user_session_html/confirm.html.heex +++ /dev/null @@ -1,70 +0,0 @@ - -
-
- <.header>{gettext("Welcome %{email}", email: @user.email)} -
- - <.form - :if={!@user.confirmed_at} - for={@form} - id="confirmation_form" - action={~p"/users/log-in?_action=confirmed"} - phx-mounted={JS.focus_first()} - > - - <.button - name={@form[:remember_me].name} - value="true" - phx-disable-with={gettext("Confirming...")} - class="btn btn-primary w-full" - > - {gettext("Confirm and stay logged in")} - - <.button - phx-disable-with={gettext("Confirming...")} - class="btn btn-primary btn-soft w-full mt-2" - > - {gettext("Confirm and log in only this time")} - - - - <.form - :if={@user.confirmed_at} - for={@form} - id="login_form" - action={~p"/users/log-in"} - phx-mounted={JS.focus_first()} - > - - <%= if @current_scope do %> - <.button - variant="primary" - phx-disable-with={gettext("Logging in...")} - class="btn btn-primary w-full" - > - {gettext("Log in")} - - <% else %> - <.button - id="remember-login-button" - name={@form[:remember_me].name} - value="true" - phx-disable-with={gettext("Logging in...")} - class="btn btn-primary w-full" - > - {gettext("Keep me logged in on this device")} - - <.button - phx-disable-with={gettext("Logging in...")} - class="btn btn-primary btn-soft w-full mt-2" - > - {gettext("Log me in only this time")} - - <% end %> - - -

- {gettext("Tip: If you prefer passwords, you can enable them in the user settings.")} -

-
-
diff --git a/lib/who_need_help_web/controllers/user_session_html/new.html.heex b/lib/who_need_help_web/controllers/user_session_html/new.html.heex index b14c348..93a19b1 100644 --- a/lib/who_need_help_web/controllers/user_session_html/new.html.heex +++ b/lib/who_need_help_web/controllers/user_session_html/new.html.heex @@ -30,47 +30,81 @@ - <.form :let={f} for={@form} as={:user} id="login_form_magic" action={~p"/users/log-in"}> - <.input - readonly={!!@current_scope} - field={f[:email]} - type="email" - label={gettext("Email")} - autocomplete="username" - spellcheck="false" - required - phx-mounted={JS.focus()} + <.form + for={@magic_link_form} + id="magic-link-fragment-form" + action={~p"/users/log-in?_action=confirmed"} + hidden + > + - <.button class="btn btn-primary w-full"> - {gettext("Log in with email")} +

+ {gettext("Your secure email link is ready. Continue to sign in.")} +

+ <.button + id="remember-login-button" + name={@magic_link_form[:remember_me].name} + value="true" + phx-disable-with={gettext("Confirming...")} + class="btn btn-primary w-full" + > + {gettext("Confirm and stay logged in")} + + <.button + phx-disable-with={gettext("Confirming...")} + class="btn btn-primary btn-soft w-full mt-2" + > + {gettext("Confirm and log in only this time")} -
{gettext("or")}
+
+ <.form :let={f} for={@form} as={:user} id="login_form_magic" action={~p"/users/log-in"}> + <.input + readonly={!!@current_scope} + field={f[:email]} + type="email" + label={gettext("Email")} + autocomplete="username" + spellcheck="false" + required + phx-mounted={JS.focus()} + /> + <.button class="btn btn-primary w-full"> + {gettext("Log in with email")} + + - <.form :let={f} for={@form} as={:user} id="login_form_password" action={~p"/users/log-in"}> - <.input - readonly={!!@current_scope} - field={f[:email]} - type="email" - label={gettext("Email")} - autocomplete="username" - spellcheck="false" - required - /> - <.input - field={f[:password]} - type="password" - label={gettext("Password")} - autocomplete="current-password" - spellcheck="false" - /> - <.button class="btn btn-primary w-full" name={@form[:remember_me].name} value="true"> - {gettext("Log in and stay logged in")} - - <.button class="btn btn-primary btn-soft w-full mt-2"> - {gettext("Log in only this time")} - - +
{gettext("or")}
+ + <.form :let={f} for={@form} as={:user} id="login_form_password" action={~p"/users/log-in"}> + <.input + readonly={!!@current_scope} + field={f[:email]} + type="email" + label={gettext("Email")} + autocomplete="username" + spellcheck="false" + required + /> + <.input + field={f[:password]} + type="password" + label={gettext("Password")} + autocomplete="current-password" + spellcheck="false" + /> + <.button class="btn btn-primary w-full" name={@form[:remember_me].name} value="true"> + {gettext("Log in and stay logged in")} + + <.button class="btn btn-primary btn-soft w-full mt-2"> + {gettext("Log in only this time")} + + +
diff --git a/lib/who_need_help_web/controllers/user_settings_controller.ex b/lib/who_need_help_web/controllers/user_settings_controller.ex index cff4cf3..7869039 100644 --- a/lib/who_need_help_web/controllers/user_settings_controller.ex +++ b/lib/who_need_help_web/controllers/user_settings_controller.ex @@ -1,44 +1,54 @@ defmodule WhoNeedHelpWeb.UserSettingsController do use WhoNeedHelpWeb, :controller + require Logger + alias WhoNeedHelp.Accounts + alias WhoNeedHelp.Trust.RateLimiter alias WhoNeedHelpWeb.UserAuth import WhoNeedHelpWeb.UserAuth, only: [require_sudo_mode: 2] - plug :require_sudo_mode - plug :assign_email_and_password_changesets + plug :require_sudo_mode when action in [:edit, :update] + plug :assign_email_and_password_changesets when action in [:edit, :update] def edit(conn, _params) do render(conn, :edit) end - def update(conn, %{"action" => "update_email"} = params) do - %{"user" => user_params} = params + def update(conn, %{"action" => "update_email", "user" => user_params}) + when is_map(user_params) do user = conn.assigns.current_scope.user case Accounts.change_user_email(user, user_params) do %{valid?: true} = changeset -> - Accounts.deliver_user_update_email_instructions( - Ecto.Changeset.apply_action!(changeset, :insert), - user.email, - &url(~p"/users/settings/confirm-email/#{&1}") - ) + new_email = + changeset + |> Ecto.Changeset.get_change(:email, "") + |> String.trim() + |> String.downcase() - conn - |> put_flash( - :info, - gettext("A link to confirm your email change has been sent to the new address.") - ) - |> redirect(to: ~p"/users/settings") + case RateLimiter.check(:email_change_email, new_email) do + {:ok, _limit} -> + deliver_email_change_instructions(conn, user, changeset) + + {:error, :rate_limited} -> + conn + |> put_status(:too_many_requests) + |> put_flash( + :error, + gettext("Too many email-change requests in the configured time window.") + ) + |> render(:edit) + end changeset -> render(conn, :edit, email_changeset: %{changeset | action: :insert}) end end - def update(conn, %{"action" => "update_password"} = params) do - %{"user" => user_params} = params + def update(conn, %{"action" => "update_password", "user" => user_params}) + when is_map(user_params) do user = conn.assigns.current_scope.user case Accounts.update_user_password(user, user_params) do @@ -56,7 +66,18 @@ defmodule WhoNeedHelpWeb.UserSettingsController do end end - def confirm_email(conn, %{"token" => token}) do + def update(conn, _params) do + conn + |> put_status(:bad_request) + |> put_flash(:error, gettext("The settings form is invalid.")) + |> render(:edit) + end + + def confirm_email_page(conn, _params) do + render(conn, :confirm_email) + end + + def confirm_email(conn, %{"token" => token}) when is_binary(token) do case Accounts.update_user_email(conn.assigns.current_scope.user, token) do {:ok, _user} -> conn @@ -70,6 +91,12 @@ defmodule WhoNeedHelpWeb.UserSettingsController do end end + def confirm_email(conn, _params) do + conn + |> put_flash(:error, gettext("Email change link is invalid or it has expired.")) + |> redirect(to: ~p"/users/settings") + end + defp assign_email_and_password_changesets(conn, _opts) do user = conn.assigns.current_scope.user @@ -77,4 +104,30 @@ defmodule WhoNeedHelpWeb.UserSettingsController do |> assign(:email_changeset, Accounts.change_user_email(user)) |> assign(:password_changeset, Accounts.change_user_password(user)) end + + defp deliver_email_change_instructions(conn, user, changeset) do + case Accounts.deliver_user_update_email_instructions( + Ecto.Changeset.apply_action!(changeset, :insert), + user.email, + &"#{url(~p"/users/settings/confirm-email")}#token=#{URI.encode_www_form(&1)}" + ) do + {:ok, _email} -> + conn + |> put_flash( + :info, + gettext("A link to confirm your email change has been sent to the new address.") + ) + |> redirect(to: ~p"/users/settings") + + {:error, _reason} -> + Logger.warning("Unable to send email-change instructions") + + conn + |> put_flash( + :error, + gettext("The confirmation email could not be sent. Please try again.") + ) + |> redirect(to: ~p"/users/settings") + end + end end diff --git a/lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex b/lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex new file mode 100644 index 0000000..9a26ac0 --- /dev/null +++ b/lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex @@ -0,0 +1,26 @@ + +
+ <.header> + {gettext("Confirm email change")} + <:subtitle>{gettext("Confirm the new email address for your account.")} + + + <.form + for={%{}} + action={~p"/users/settings/confirm-email"} + id="email-change-fragment-form" + hidden + > + + <.button variant="primary" phx-disable-with={gettext("Confirming...")}> + {gettext("Confirm email change")} + + + + +
+
diff --git a/lib/who_need_help_web/endpoint.ex b/lib/who_need_help_web/endpoint.ex index 7cf5381..32215ca 100644 --- a/lib/who_need_help_web/endpoint.ex +++ b/lib/who_need_help_web/endpoint.ex @@ -8,7 +8,8 @@ defmodule WhoNeedHelpWeb.Endpoint do store: :cookie, key: "_who_need_help_key", signing_salt: "XN+IwK3w", - same_site: "Lax" + same_site: "Lax", + secure: Application.compile_env(:who_need_help, :secure_cookies, false) ] socket "/live", Phoenix.LiveView.Socket, @@ -45,8 +46,7 @@ defmodule WhoNeedHelpWeb.Endpoint do plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] plug Plug.Parsers, - parsers: [:urlencoded, :multipart, :json], - pass: ["*/*"], + parsers: [:urlencoded, :json], json_decoder: Phoenix.json_library() plug Plug.MethodOverride diff --git a/lib/who_need_help_web/live/activity_live/index.ex b/lib/who_need_help_web/live/activity_live/index.ex index 26a1586..9da9f8f 100644 --- a/lib/who_need_help_web/live/activity_live/index.ex +++ b/lib/who_need_help_web/live/activity_live/index.ex @@ -1,12 +1,15 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do use WhoNeedHelpWeb, :live_view - alias WhoNeedHelp.{Activities, Catalog} + alias WhoNeedHelp.{Activities, Catalog, Pagination} alias WhoNeedHelp.Activities.Activity @impl true def mount(_params, _session, socket) do - if connected?(socket), do: Activities.subscribe() + if connected?(socket) do + Activities.subscribe() + Activities.subscribe_user(socket.assigns.current_scope) + end {:ok, socket @@ -18,8 +21,9 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do def handle_info({event, activity}, socket) when event in [:activity_created, :activity_updated] do user_id = socket.assigns.current_scope.user.id + already_mine? = Enum.any?(socket.assigns.my_activities, &(&1.id == activity.id)) - activities = + {activities, activities_cursor} = update_entry( socket.assigns.activities, activity, @@ -28,24 +32,44 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do activity, socket.assigns.filters ), - :asc + :asc, + socket.assigns.activities_cursor ) - my_activities = + {my_activities, my_activities_cursor} = update_entry( socket.assigns.my_activities, activity, - Activities.member_activity?(activity, user_id), - :desc + already_mine? or activity.creator_id == user_id, + :desc, + socket.assigns.my_activities_cursor ) {:noreply, socket |> assign(:activities, activities) + |> assign(:activities_cursor, activities_cursor) |> assign(:my_activities, my_activities) + |> assign(:my_activities_cursor, my_activities_cursor) |> assign(:markers, Jason.encode!(Enum.flat_map(activities, &List.wrap(marker(&1)))))} end + def handle_info({:my_activity_updated, activity, member?}, socket) do + {my_activities, my_activities_cursor} = + update_entry( + socket.assigns.my_activities, + activity, + member?, + :desc, + socket.assigns.my_activities_cursor + ) + + {:noreply, + socket + |> assign(:my_activities, my_activities) + |> assign(:my_activities_cursor, my_activities_cursor)} + end + @impl true def handle_event("filter", %{"filters" => filters}, socket) do {:noreply, socket |> assign(:filters, filters) |> load()} @@ -106,10 +130,23 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do existing ++ Enum.reject(incoming, &MapSet.member?(existing_ids, &1.id)) end - defp update_entry(entries, activity, visible?, direction) do + defp update_entry(entries, activity, visible?, direction, cursor) do + window_limit = max(length(entries), Pagination.limit([])) entries = Enum.reject(entries, &(&1.id == activity.id)) entries = if visible?, do: [activity | entries], else: entries - Enum.sort_by(entries, &{&1.starts_at, &1.id}, direction) + entries = Enum.sort_by(entries, &{&1.starts_at, &1.id}, direction) + overflow? = length(entries) > window_limit + entries = Enum.take(entries, window_limit) + + cursor = + if overflow? do + activity = List.last(entries) + Pagination.encode(activity.starts_at, activity.id) + else + cursor + end + + {entries, cursor} end defp marker(activity) do @@ -195,6 +232,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do <.link :for={activity <- @activities} + id={"open-activity-#{activity.id}"} navigate={~p"/activities/#{activity.id}"} class="block rounded-3xl border border-base-300 bg-base-100 p-6 transition hover:border-info/50" > @@ -255,6 +293,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
<.link :for={activity <- @my_activities} + id={"my-activity-#{activity.id}"} navigate={~p"/activities/#{activity.id}"} class="rounded-2xl bg-base-200 p-4" > diff --git a/lib/who_need_help_web/live/activity_live/new.ex b/lib/who_need_help_web/live/activity_live/new.ex index abd25f4..e54c991 100644 --- a/lib/who_need_help_web/live/activity_live/new.ex +++ b/lib/who_need_help_web/live/activity_live/new.ex @@ -90,7 +90,10 @@ defmodule WhoNeedHelpWeb.ActivityLive.New do defp option_value(option), do: option defp structured_value(form, key) do - form.params |> Map.get("structured_data", %{}) |> Map.get(key) + case Map.get(form.params, "structured_data") do + structured_data when is_map(structured_data) -> Map.get(structured_data, key) + _invalid_or_missing -> nil + end end defp error_message(:account_not_eligible), diff --git a/lib/who_need_help_web/live/activity_live/show.ex b/lib/who_need_help_web/live/activity_live/show.ex index 77cdd4b..20d6275 100644 --- a/lib/who_need_help_web/live/activity_live/show.ex +++ b/lib/who_need_help_web/live/activity_live/show.ex @@ -1,7 +1,9 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do use WhoNeedHelpWeb, :live_view - alias WhoNeedHelp.{Activities, Trust} + alias WhoNeedHelp.{Activities, Pagination, Trust} + + @initial_message_window 50 @impl true def mount(%{"id" => id}, _session, socket) do @@ -20,9 +22,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do @impl true def handle_info({:activity_message, message}, socket) do - activity = socket.assigns.activity - messages = append_message(activity.messages, message) - {:noreply, assign(socket, :activity, %{activity | messages: messages})} + {:noreply, put_realtime_message(socket, message)} end def handle_info({event, _payload}, socket) @@ -80,13 +80,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do params ) do {:ok, message} -> - activity = socket.assigns.activity - - {:noreply, - assign(socket, :activity, %{ - activity - | messages: append_message(activity.messages, message) - })} + {:noreply, put_realtime_message(socket, message)} {:error, reason} -> {:noreply, put_flash(socket, :error, error_message(reason))} @@ -108,7 +102,8 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do {:noreply, socket |> assign(:activity, %{activity | messages: older ++ activity.messages}) - |> assign(:messages_cursor, page.next_cursor)} + |> assign(:messages_cursor, page.next_cursor) + |> update(:message_window_limit, &(&1 + length(older)))} end def handle_event("report", %{"report" => params}, socket) do @@ -212,6 +207,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do |> assign(:page_title, activity.title) |> assign(:activity, activity) |> assign(:messages_cursor, messages.next_cursor) + |> assign(:message_window_limit, max(length(messages.entries), @initial_message_window)) |> assign(:viewer_participant, viewer_participant) |> assign(:organizer?, organizer?) |> assign(:approved?, approved?) @@ -246,6 +242,23 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do if Enum.any?(messages, &(&1.id == message.id)), do: messages, else: messages ++ [message] end + defp put_realtime_message(socket, message) do + activity = socket.assigns.activity + messages = append_message(activity.messages, message) + window_limit = socket.assigns.message_window_limit + + if length(messages) > window_limit do + messages = Enum.take(messages, -window_limit) + oldest = hd(messages) + + socket + |> assign(:activity, %{activity | messages: messages}) + |> assign(:messages_cursor, Pagination.encode(oldest.inserted_at, oldest.id)) + else + assign(socket, :activity, %{activity | messages: messages}) + end + end + defp structured_details(activity, locale) do activity.category |> WhoNeedHelp.Catalog.structured_fields() @@ -389,7 +402,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do :for={identity <- @activity.creator.social_identities} href={identity.profile_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class={[ "badge badge-outline", identity.verified_at && "badge-success", @@ -540,7 +553,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do :for={identity <- participant.user.social_identities} href={identity.profile_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class={[ "badge badge-xs badge-outline", identity.verified_at && "badge-success", @@ -599,7 +612,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do :for={identity <- participant.user.social_identities} href={identity.profile_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class={[ "badge badge-xs badge-outline", identity.verified_at && "badge-success", diff --git a/lib/who_need_help_web/live/moderation_live.ex b/lib/who_need_help_web/live/moderation_live.ex index fc1b9f8..daa5648 100644 --- a/lib/who_need_help_web/live/moderation_live.ex +++ b/lib/who_need_help_web/live/moderation_live.ex @@ -100,7 +100,7 @@ defmodule WhoNeedHelpWeb.ModerationLive do } |> Map.reject(fn {_locale, description} -> description in [nil, ""] end) - with {:ok, structured_fields} <- Jason.decode(params["structured_fields_json"] || "{}"), + with {:ok, structured_fields} <- decode_structured_fields(params["structured_fields_json"]), true <- is_map(structured_fields) do attrs = %{ "slug" => params["slug"], @@ -190,6 +190,10 @@ defmodule WhoNeedHelpWeb.ModerationLive do {:noreply, put_flash(socket, :error, error_message(reason))} end + defp decode_structured_fields(value) when value in [nil, ""], do: {:ok, %{}} + defp decode_structured_fields(value) when is_binary(value), do: Jason.decode(value) + defp decode_structured_fields(_value), do: {:error, :invalid_type} + defp load(socket) do reports = Trust.paginate_reports(socket.assigns.current_scope) signals = Trust.paginate_abuse_signals(socket.assigns.current_scope) diff --git a/lib/who_need_help_web/live/profile_live.ex b/lib/who_need_help_web/live/profile_live.ex index 3ccad85..547233f 100644 --- a/lib/who_need_help_web/live/profile_live.ex +++ b/lib/who_need_help_web/live/profile_live.ex @@ -44,15 +44,22 @@ defmodule WhoNeedHelpWeb.ProfileLive do def handle_event("add-social", %{"social_identity" => params}, socket) do user = socket.assigns.current_scope.user - case Accounts.add_social_identity(user, params) do - {:ok, _identity} -> - {:noreply, - socket - |> assign_social_identities(user) - |> put_flash(:info, gettext("Social link added as unverified."))} - - {:error, changeset} -> + with {:ok, _limit} <- Trust.authorize_action(socket.assigns.current_scope, :add_social), + {:ok, _identity} <- Accounts.add_social_identity(user, params) do + {:noreply, + socket + |> assign_social_identities(user) + |> put_flash(:info, gettext("Social link added as unverified."))} + else + {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, :social_form, to_form(changeset, as: :social_identity))} + + {:error, :rate_limited} -> + {:noreply, + put_flash(socket, :error, gettext("Too many actions in the configured time window."))} + + {:error, :account_not_eligible} -> + {:noreply, put_flash(socket, :error, gettext("This account cannot perform that action."))} end end @@ -273,7 +280,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do {identity.handle || identity.profile_url} diff --git a/lib/who_need_help_web/live/request_live/index.ex b/lib/who_need_help_web/live/request_live/index.ex index 4dc29f7..e169f86 100644 --- a/lib/who_need_help_web/live/request_live/index.ex +++ b/lib/who_need_help_web/live/request_live/index.ex @@ -1,7 +1,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do use WhoNeedHelpWeb, :live_view - alias WhoNeedHelp.{Help, Trust} + alias WhoNeedHelp.{Help, Pagination, Trust} @impl true def mount(_params, _session, socket) do @@ -17,7 +17,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do def handle_info({event, request}, socket) when event in [:request_created, :request_updated] do user = socket.assigns.current_scope.user - requests = + {requests, requests_cursor} = update_entry( socket.assigns.requests, request, @@ -26,21 +26,25 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do request, socket.assigns.filters ), - :open + :open, + socket.assigns.requests_cursor ) - my_requests = + {my_requests, my_requests_cursor} = update_entry( socket.assigns.my_requests, request, request.requester_id == user.id, - :mine + :mine, + socket.assigns.my_requests_cursor ) socket = socket |> assign(:requests, requests) + |> assign(:requests_cursor, requests_cursor) |> assign(:my_requests, my_requests) + |> assign(:my_requests_cursor, my_requests_cursor) |> assign(:markers, Jason.encode!(Enum.flat_map(requests, &List.wrap(marker(&1))))) |> maybe_refresh_reputation(request, user.id) @@ -106,7 +110,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do existing ++ Enum.reject(incoming, &MapSet.member?(existing_ids, &1.id)) end - defp update_entry(entries, request, visible?, order) do + defp update_entry(entries, request, visible?, order, cursor) do + window_limit = max(length(entries), Pagination.limit([])) entries = Enum.reject(entries, &(&1.id == request.id)) entries = @@ -114,10 +119,25 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do do: [request | entries], else: entries - case order do - :open -> Enum.sort_by(entries, &{&1.expires_at, &1.id}, :asc) - :mine -> Enum.sort_by(entries, &{&1.inserted_at, &1.id}, :desc) - end + entries = + case order do + :open -> Enum.sort_by(entries, &{&1.expires_at, &1.id}, :asc) + :mine -> Enum.sort_by(entries, &{&1.inserted_at, &1.id}, :desc) + end + + overflow? = length(entries) > window_limit + entries = Enum.take(entries, window_limit) + + cursor = + if overflow? do + request = List.last(entries) + timestamp = if order == :open, do: request.expires_at, else: request.inserted_at + Pagination.encode(timestamp, request.id) + else + cursor + end + + {entries, cursor} end defp maybe_refresh_reputation(socket, request, user_id) do @@ -240,6 +260,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do <.link :for={request <- @requests} + id={"open-request-#{request.id}"} navigate={~p"/requests/#{request.id}"} class="help-card block rounded-3xl border border-base-300 bg-base-100 p-6" > @@ -322,6 +343,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
<.link :for={request <- @my_requests} + id={"my-request-#{request.id}"} navigate={~p"/requests/#{request.id}"} class="rounded-2xl bg-base-200 p-4" > diff --git a/lib/who_need_help_web/live/request_live/new.ex b/lib/who_need_help_web/live/request_live/new.ex index 829b14b..97c2729 100644 --- a/lib/who_need_help_web/live/request_live/new.ex +++ b/lib/who_need_help_web/live/request_live/new.ex @@ -98,7 +98,10 @@ defmodule WhoNeedHelpWeb.RequestLive.New do defp option_value(option), do: option defp structured_value(form, key) do - form.params |> Map.get("structured_data", %{}) |> Map.get(key) + case Map.get(form.params, "structured_data") do + structured_data when is_map(structured_data) -> Map.get(structured_data, key) + _invalid_or_missing -> nil + end end defp category_description(nil, _locale), do: nil diff --git a/lib/who_need_help_web/live/request_live/show.ex b/lib/who_need_help_web/live/request_live/show.ex index 899948f..3120a01 100644 --- a/lib/who_need_help_web/live/request_live/show.ex +++ b/lib/who_need_help_web/live/request_live/show.ex @@ -1,10 +1,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do use WhoNeedHelpWeb, :live_view - alias WhoNeedHelp.{Help, Messaging, Tracking, Trust} + alias WhoNeedHelp.{Help, Messaging, Pagination, Tracking, Trust} alias WhoNeedHelpWeb.Presence @e2e_routes Application.compile_env(:who_need_help, :e2e_routes, false) + @initial_message_window 50 @impl true def mount(%{"id" => id}, _session, socket) do @@ -76,7 +77,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do end def handle_info({:new_message, message}, socket) do - {:noreply, assign(socket, :messages, append_message(socket.assigns.messages, message))} + {:noreply, put_realtime_message(socket, message)} end def handle_info({:position_updated, user_id, position, evidence}, socket) do @@ -155,7 +156,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do {:ok, message} -> {:noreply, socket - |> assign(:messages, append_message(socket.assigns.messages, message)) + |> put_realtime_message(message) |> push_event("reset-message-form", %{id: "message-form"})} {:error, reason} -> @@ -177,7 +178,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do {:noreply, socket |> assign(:messages, older ++ socket.assigns.messages) - |> assign(:messages_cursor, page.next_cursor)} + |> assign(:messages_cursor, page.next_cursor) + |> update(:message_window_limit, &(&1 + length(older)))} end def handle_event("start-tracking", _, socket) do @@ -427,6 +429,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do ) |> assign(:messages, messages_page.entries) |> assign(:messages_cursor, messages_page.next_cursor) + |> assign(:message_window_limit, max(length(messages_page.entries), @initial_message_window)) |> assign(:report_message_id, nil) |> assign(:positions, positions) |> assign(:tracking_active, tracking_active) @@ -446,6 +449,22 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do if Enum.any?(messages, &(&1.id == message.id)), do: messages, else: messages ++ [message] end + defp put_realtime_message(socket, message) do + messages = append_message(socket.assigns.messages, message) + window_limit = socket.assigns.message_window_limit + + if length(messages) > window_limit do + messages = Enum.take(messages, -window_limit) + oldest = hd(messages) + + socket + |> assign(:messages, messages) + |> assign(:messages_cursor, Pagination.encode(oldest.inserted_at, oldest.id)) + else + assign(socket, :messages, messages) + end + end + defp report_form do to_form(%{"reason" => "dangerous_request", "details" => ""}, as: :report) end @@ -570,8 +589,12 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do latitude: point.latitude, longitude: point.longitude, exact: true, - title: gettext("Shared live location"), - location: gettext("Active match only") + title: gettext("Shared location"), + location: + gettext( + "Last update: %{time}", + time: Calendar.strftime(point.captured_at, "%d.%m.%Y, %H:%M:%S UTC") + ) } end) end @@ -704,7 +727,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do :for={identity <- @request.requester.social_identities} href={identity.profile_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class="badge badge-warning badge-outline" > {identity.provider} · {if identity.verified_at, @@ -901,7 +924,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do :for={identity <- @assignment.helper.social_identities} href={identity.profile_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class="badge badge-warning badge-outline" > {identity.provider} · {if identity.verified_at, @@ -982,7 +1005,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do } href={@assignment.helper.tip_url} target="_blank" - rel="noopener noreferrer nofollow" + rel="noopener noreferrer nofollow ugc" class="btn btn-outline w-full" > {gettext("Optional thank-you link")} diff --git a/lib/who_need_help_web/router.ex b/lib/who_need_help_web/router.ex index f2a4975..b7d6413 100644 --- a/lib/who_need_help_web/router.ex +++ b/lib/who_need_help_web/router.ex @@ -3,13 +3,19 @@ defmodule WhoNeedHelpWeb.Router do import WhoNeedHelpWeb.UserAuth + @secure_browser_headers %{ + "content-security-policy" => + "default-src 'self'; base-uri 'self'; frame-ancestors 'none'; object-src 'none'", + "permissions-policy" => "geolocation=(self), camera=(), microphone=(), payment=(), usb=()" + } + pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {WhoNeedHelpWeb.Layouts, :root} plug :protect_from_forgery - plug :put_secure_browser_headers + plug :put_secure_browser_headers, @secure_browser_headers plug :put_content_security_policy plug :fetch_current_scope_for_user plug :put_authenticated_cache_policy @@ -24,7 +30,7 @@ defmodule WhoNeedHelpWeb.Router do plug :accepts, ["json"] plug :fetch_session plug :protect_from_forgery - plug :put_secure_browser_headers + plug :put_secure_browser_headers, @secure_browser_headers plug :put_content_security_policy plug :fetch_current_scope_for_user plug :put_authenticated_cache_policy @@ -104,7 +110,8 @@ defmodule WhoNeedHelpWeb.Router do get "/users/settings", UserSettingsController, :edit put "/users/settings", UserSettingsController, :update - get "/users/settings/confirm-email/:token", UserSettingsController, :confirm_email + get "/users/settings/confirm-email", UserSettingsController, :confirm_email_page + post "/users/settings/confirm-email", UserSettingsController, :confirm_email get "/auth/social/:provider", SocialOAuthController, :request get "/auth/social/:provider/callback", SocialOAuthController, :callback end @@ -135,7 +142,6 @@ defmodule WhoNeedHelpWeb.Router do pipe_through [:browser] get "/users/log-in", UserSessionController, :new - get "/users/log-in/:token", UserSessionController, :confirm post "/users/log-in", UserSessionController, :create delete "/users/log-out", UserSessionController, :delete end diff --git a/lib/who_need_help_web/user_auth.ex b/lib/who_need_help_web/user_auth.ex index 9eb5565..6d45bfe 100644 --- a/lib/who_need_help_web/user_auth.ex +++ b/lib/who_need_help_web/user_auth.ex @@ -15,7 +15,8 @@ defmodule WhoNeedHelpWeb.UserAuth do @remember_me_options [ sign: true, max_age: @max_cookie_age_in_days * 24 * 60 * 60, - same_site: "Lax" + same_site: "Lax", + secure: Application.compile_env(:who_need_help, :secure_cookies, false) ] # How old the session token should be before a new one is issued. When a request is made diff --git a/lib/who_need_help_web/validation_messages.ex b/lib/who_need_help_web/validation_messages.ex index a9b6ead..367965b 100644 --- a/lib/who_need_help_web/validation_messages.ex +++ b/lib/who_need_help_web/validation_messages.ex @@ -15,7 +15,8 @@ defmodule WhoNeedHelpWeb.ValidationMessages do dgettext_noop("errors", "must be in the future"), dgettext_noop("errors", "select an activity category"), dgettext_noop("errors", "must be on or before the start time"), - dgettext_noop("errors", "must be a complete http(s) URL"), + dgettext_noop("errors", "must be a complete HTTPS URL"), + dgettext_noop("errors", "must be a full HTTPS URL"), dgettext_noop("errors", "must be a GitHub profile URL"), dgettext_noop("errors", "you must confirm that you are 18+ and accept the rules"), dgettext_noop("errors", "must have the @ sign and no spaces"), diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 403874c..c66ac0b 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -11,30 +11,32 @@ msgid "" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:67 +#: lib/who_need_help_web/components/layouts.ex:111 +#: lib/who_need_help_web/components/layouts.ex:257 #, elixir-autogen, elixir-format msgid "Account settings" msgstr "" -#: lib/who_need_help_web/components/core_components.ex:375 +#: lib/who_need_help_web/components/core_components.ex:393 #, elixir-autogen, elixir-format msgid "Actions" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:83 +#: lib/who_need_help_web/components/layouts.ex:70 +#: lib/who_need_help_web/components/layouts.ex:119 #: lib/who_need_help_web/controllers/page_html/home.html.heex:21 #: lib/who_need_help_web/live/request_live/new.ex:22 #, elixir-autogen, elixir-format msgid "Ask for help" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:140 -#: lib/who_need_help_web/components/layouts.ex:155 +#: lib/who_need_help_web/components/layouts.ex:295 +#: lib/who_need_help_web/components/layouts.ex:310 #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:59 +#: lib/who_need_help_web/components/layouts.ex:171 #, elixir-autogen, elixir-format msgid "Categories" msgstr "" @@ -54,33 +56,34 @@ msgstr "" msgid "Join the community" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:89 -#: lib/who_need_help_web/components/layouts/root.html.heex:77 +#: lib/who_need_help_web/components/layouts.ex:78 +#: lib/who_need_help_web/components/layouts.ex:128 #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:9 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:45 #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:5 #, elixir-autogen, elixir-format msgid "Log in" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:70 +#: lib/who_need_help_web/components/layouts.ex:114 +#: lib/who_need_help_web/components/layouts.ex:260 #, elixir-autogen, elixir-format msgid "Log out" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:78 -#: lib/who_need_help_web/components/layouts/root.html.heex:64 +#: lib/who_need_help_web/components/layouts.ex:108 +#: lib/who_need_help_web/components/layouts.ex:254 #: lib/who_need_help_web/live/profile_live.ex:16 #, elixir-autogen, elixir-format msgid "Profile" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:74 +#: lib/who_need_help_web/components/layouts.ex:75 +#: lib/who_need_help_web/components/layouts.ex:125 #, elixir-autogen, elixir-format msgid "Register" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:49 +#: lib/who_need_help_web/components/layouts.ex:161 #, elixir-autogen, elixir-format msgid "Requests" msgstr "" @@ -90,12 +93,12 @@ msgstr "" msgid "See nearby requests" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:147 +#: lib/who_need_help_web/components/layouts.ex:302 #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:132 +#: lib/who_need_help_web/components/layouts.ex:287 #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "" @@ -105,62 +108,62 @@ msgstr "" msgid "close" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:344 +#: lib/who_need_help_web/live/activity_live/show.ex:363 #, elixir-autogen, elixir-format msgid "%{approved}/%{capacity} approved" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:481 +#: lib/who_need_help_web/live/moderation_live.ex:485 #, elixir-autogen, elixir-format msgid "%{count} community votes" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:637 +#: lib/who_need_help_web/live/request_live/show.ex:745 #, elixir-autogen, elixir-format msgid "%{count} unique people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:718 +#: lib/who_need_help_web/live/request_live/show.ex:829 #, elixir-autogen, elixir-format msgid "1 — unsafe" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:717 +#: lib/who_need_help_web/live/request_live/show.ex:828 #, elixir-autogen, elixir-format msgid "2 — poor" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:716 +#: lib/who_need_help_web/live/request_live/show.ex:827 #, elixir-autogen, elixir-format msgid "3 — okay" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:715 +#: lib/who_need_help_web/live/request_live/show.ex:826 #, elixir-autogen, elixir-format msgid "4 — good" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:714 +#: lib/who_need_help_web/live/request_live/show.ex:825 #, elixir-autogen, elixir-format msgid "5 — excellent" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:31 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:118 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:433 +#: lib/who_need_help_web/live/moderation_live.ex:437 #, elixir-autogen, elixir-format msgid "A signal requests human review; optional tracking absence is not proof of abuse." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:748 +#: lib/who_need_help_web/live/request_live/show.ex:859 #, elixir-autogen, elixir-format msgid "Accept only if you can complete this safely and for free." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:535 +#: lib/who_need_help_web/live/request_live/show.ex:633 #, elixir-autogen, elixir-format msgid "Accepted" msgstr "" @@ -170,34 +173,30 @@ msgstr "" msgid "Account Settings" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:54 +#: lib/who_need_help_web/components/layouts.ex:63 +#: lib/who_need_help_web/components/layouts.ex:192 #, elixir-autogen, elixir-format msgid "Account and language" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:565 +#: lib/who_need_help_web/live/moderation_live.ex:569 #, elixir-autogen, elixir-format msgid "Accounts" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:744 +#: lib/who_need_help_web/live/request_live/show.ex:855 #, elixir-autogen, elixir-format msgid "Action center" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:261 -#: lib/who_need_help_web/live/moderation_live.ex:610 +#: lib/who_need_help_web/live/moderation_live.ex:265 +#: lib/who_need_help_web/live/moderation_live.ex:614 #, elixir-autogen, elixir-format msgid "Active" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:502 -#, elixir-autogen, elixir-format -msgid "Active match only" -msgstr "" - -#: lib/who_need_help_web/components/layouts.ex:54 -#: lib/who_need_help_web/live/activity_live/index.ex:68 +#: lib/who_need_help_web/components/layouts.ex:166 +#: lib/who_need_help_web/live/activity_live/index.ex:118 #, elixir-autogen, elixir-format msgid "Activities" msgstr "" @@ -207,7 +206,7 @@ msgstr "" msgid "Activities are optional plans, not urgent help, and do not affect helper reputation. Meet in a suitable public place first. Organizers approve every participant; exact coordinates and group chat are limited to approved participants. Share route and safety plans for hikes, and never publish home addresses, access codes, or travel documents." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:161 +#: lib/who_need_help_web/live/activity_live/new.ex:163 #, elixir-autogen, elixir-format msgid "Activity details" msgstr "" @@ -217,8 +216,8 @@ msgstr "" msgid "Activity hidden." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:16 -#: lib/who_need_help_web/live/activity_live/show.ex:182 +#: lib/who_need_help_web/live/activity_live/show.ex:18 +#: lib/who_need_help_web/live/activity_live/show.ex:177 #, elixir-autogen, elixir-format msgid "Activity is unavailable." msgstr "" @@ -233,29 +232,29 @@ msgstr "" msgid "Activity restored." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:140 -#: lib/who_need_help_web/live/activity_live/new.ex:137 +#: lib/who_need_help_web/live/activity_live/index.ex:210 +#: lib/who_need_help_web/live/activity_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Activity type" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:322 +#: lib/who_need_help_web/live/profile_live.ex:325 #, elixir-autogen, elixir-format msgid "Add unverified link" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:268 -#: lib/who_need_help_web/live/moderation_live.ex:634 +#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:638 #, elixir-autogen, elixir-format msgid "Administrator" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:142 +#: lib/who_need_help_web/live/activity_live/index.ex:212 #, elixir-autogen, elixir-format msgid "All activities" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:152 +#: lib/who_need_help_web/live/request_live/index.ex:227 #, elixir-autogen, elixir-format msgid "All categories" msgstr "" @@ -265,70 +264,65 @@ msgstr "" msgid "Already registered?" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:29 -#, elixir-autogen, elixir-format -msgid "An email was sent to %{email}, please access it to confirm your account." -msgstr "" - -#: lib/who_need_help_web/live/request_live/show.ex:508 +#: lib/who_need_help_web/live/request_live/show.ex:603 #, elixir-autogen, elixir-format msgid "Another helper already accepted this request." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:167 +#: lib/who_need_help_web/live/request_live/index.ex:242 #, elixir-autogen, elixir-format msgid "Any urgency" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:505 +#: lib/who_need_help_web/live/activity_live/show.ex:524 #, elixir-autogen, elixir-format msgid "Approval pending" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:555 +#: lib/who_need_help_web/live/activity_live/show.ex:574 #, elixir-autogen, elixir-format msgid "Approve" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:416 +#: lib/who_need_help_web/live/activity_live/show.ex:435 #, elixir-autogen, elixir-format msgid "Approved group chat" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:581 +#: lib/who_need_help_web/live/activity_live/show.ex:600 #, elixir-autogen, elixir-format msgid "Approved participants" msgstr "" #: lib/who_need_help_web/controllers/page_html/home.html.heex:51 -#: lib/who_need_help_web/live/activity_live/new.ex:256 -#: lib/who_need_help_web/live/request_live/new.ex:304 +#: lib/who_need_help_web/live/activity_live/new.ex:258 +#: lib/who_need_help_web/live/request_live/new.ex:306 #, elixir-autogen, elixir-format msgid "Approximate area" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Approximate public meeting area" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:164 -#: lib/who_need_help_web/live/request_live/show.ex:541 +#: lib/who_need_help_web/live/profile_live.ex:167 +#: lib/who_need_help_web/live/request_live/show.ex:639 #, elixir-autogen, elixir-format msgid "Approximate publicly" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:312 +#: lib/who_need_help_web/live/request_live/new.ex:314 #, elixir-autogen, elixir-format msgid "Approximate publicly (recommended)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:594 +#: lib/who_need_help_web/live/request_live/show.ex:702 #, elixir-autogen, elixir-format msgid "Area:" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:152 +#: lib/who_need_help_web/live/request_live/new.ex:154 #, elixir-autogen, elixir-format msgid "Ask for urgent help" msgstr "" @@ -348,17 +342,17 @@ msgstr "" msgid "Block and report" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:622 +#: lib/who_need_help_web/live/activity_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Block organizer" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:949 +#: lib/who_need_help_web/live/request_live/show.ex:1062 #, elixir-autogen, elixir-format msgid "Block this user" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:328 +#: lib/who_need_help_web/live/profile_live.ex:331 #, elixir-autogen, elixir-format msgid "Blocked users" msgstr "" @@ -368,12 +362,12 @@ msgstr "" msgid "Both confirm and the helper enters a one-time code." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:115 +#: lib/who_need_help_web/live/request_live/new.ex:118 #, elixir-autogen, elixir-format msgid "Briefly describe the help you need" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:106 +#: lib/who_need_help_web/components/layouts.ex:147 #, elixir-autogen, elixir-format msgid "Build Week feedback" msgstr "" @@ -393,28 +387,28 @@ msgstr "" msgid "COMMUNITY-DRIVEN TAXONOMY" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:575 +#: lib/who_need_help_web/live/activity_live/show.ex:594 #, elixir-autogen, elixir-format msgid "Cancel activity" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:762 -#: lib/who_need_help_web/live/request_live/show.ex:856 +#: lib/who_need_help_web/live/request_live/show.ex:873 +#: lib/who_need_help_web/live/request_live/show.ex:967 #, elixir-autogen, elixir-format msgid "Cancel request" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:99 -#: lib/who_need_help_web/live/activity_live/show.ex:321 -#: lib/who_need_help_web/live/request_live/index.ex:110 -#: lib/who_need_help_web/live/request_live/index.ex:111 -#: lib/who_need_help_web/live/request_live/show.ex:538 +#: lib/who_need_help_web/live/activity_live/index.ex:168 +#: lib/who_need_help_web/live/activity_live/show.ex:340 +#: lib/who_need_help_web/live/request_live/index.ex:184 +#: lib/who_need_help_web/live/request_live/index.ex:185 +#: lib/who_need_help_web/live/request_live/show.ex:636 #, elixir-autogen, elixir-format msgid "Cancelled" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:150 -#: lib/who_need_help_web/live/request_live/new.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:225 +#: lib/who_need_help_web/live/request_live/new.ex:180 #, elixir-autogen, elixir-format msgid "Category" msgstr "" @@ -424,14 +418,14 @@ msgstr "" msgid "Category created and proposal approved." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:209 -#: lib/who_need_help_web/live/request_live/show.ex:583 +#: lib/who_need_help_web/live/request_live/new.ex:211 +#: lib/who_need_help_web/live/request_live/show.ex:691 #, elixir-autogen, elixir-format msgid "Category details" msgstr "" #: lib/who_need_help_web/live/category_proposal_live.ex:66 -#: lib/who_need_help_web/live/moderation_live.ex:462 +#: lib/who_need_help_web/live/moderation_live.ex:466 #, elixir-autogen, elixir-format msgid "Category proposals" msgstr "" @@ -441,7 +435,7 @@ msgstr "" msgid "Change Email" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:638 +#: lib/who_need_help_web/live/moderation_live.ex:642 #, elixir-autogen, elixir-format msgid "Change role" msgstr "" @@ -457,51 +451,51 @@ msgstr "" msgid "Choose a category, urgency, and safe location visibility." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:138 +#: lib/who_need_help_web/live/activity_live/new.ex:140 #, elixir-autogen, elixir-format msgid "Choose activity" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:179 +#: lib/who_need_help_web/live/request_live/new.ex:181 #, elixir-autogen, elixir-format msgid "Choose category" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:173 -#: lib/who_need_help_web/live/activity_live/new.ex:200 -#: lib/who_need_help_web/live/request_live/new.ex:221 -#: lib/who_need_help_web/live/request_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:175 +#: lib/who_need_help_web/live/activity_live/new.ex:202 +#: lib/who_need_help_web/live/request_live/new.ex:223 +#: lib/who_need_help_web/live/request_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Choose…" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:249 +#: lib/who_need_help_web/live/activity_live/new.ex:251 #, elixir-autogen, elixir-format msgid "City centre, near the main entrance" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:635 -#: lib/who_need_help_web/live/request_live/show.ex:965 +#: lib/who_need_help_web/live/activity_live/show.ex:654 +#: lib/who_need_help_web/live/request_live/show.ex:1078 #, elixir-autogen, elixir-format msgid "Clear" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:387 +#: lib/who_need_help_web/live/moderation_live.ex:391 #, elixir-autogen, elixir-format msgid "Close" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:218 +#: lib/who_need_help_web/live/activity_live/new.ex:220 #, elixir-autogen, elixir-format msgid "Coffee and conversation" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:113 +#: lib/who_need_help_web/live/activity_live/index.ex:182 #, elixir-autogen, elixir-format msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:724 +#: lib/who_need_help_web/live/request_live/show.ex:835 #, elixir-autogen, elixir-format msgid "Comment (optional)" msgstr "" @@ -511,35 +505,35 @@ msgstr "" msgid "Community helpers" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:191 -#: lib/who_need_help_web/live/activity_live/show.ex:373 -#: lib/who_need_help_web/live/activity_live/show.ex:527 -#: lib/who_need_help_web/live/activity_live/show.ex:584 -#: lib/who_need_help_web/live/request_live/index.ex:211 +#: lib/who_need_help_web/live/activity_live/index.ex:262 +#: lib/who_need_help_web/live/activity_live/show.ex:392 +#: lib/who_need_help_web/live/activity_live/show.ex:546 +#: lib/who_need_help_web/live/activity_live/show.ex:603 +#: lib/who_need_help_web/live/request_live/index.ex:287 #, elixir-autogen, elixir-format msgid "Community member" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:98 -#: lib/who_need_help_web/live/activity_live/show.ex:320 -#: lib/who_need_help_web/live/request_live/index.ex:108 -#: lib/who_need_help_web/live/request_live/index.ex:109 -#: lib/who_need_help_web/live/request_live/show.ex:537 +#: lib/who_need_help_web/live/activity_live/index.ex:167 +#: lib/who_need_help_web/live/activity_live/show.ex:339 +#: lib/who_need_help_web/live/request_live/index.ex:182 +#: lib/who_need_help_web/live/request_live/index.ex:183 +#: lib/who_need_help_web/live/request_live/show.ex:635 #, elixir-autogen, elixir-format msgid "Completed" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:27 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:61 #, elixir-autogen, elixir-format msgid "Confirm and log in only this time" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:21 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:55 #, elixir-autogen, elixir-format msgid "Confirm and stay logged in" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:836 +#: lib/who_need_help_web/live/request_live/show.ex:947 #, elixir-autogen, elixir-format msgid "Confirm my part is complete" msgstr "" @@ -550,13 +544,13 @@ msgid "Confirm new password" msgstr "" #: lib/who_need_help_web/live/category_proposal_live.ex:51 -#: lib/who_need_help_web/live/request_live/show.ex:516 +#: lib/who_need_help_web/live/request_live/show.ex:611 #, elixir-autogen, elixir-format msgid "Confirm your account and ensure it is active first." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:97 -#: lib/who_need_help_web/live/request_live/new.ex:136 +#: lib/who_need_help_web/live/activity_live/new.ex:100 +#: lib/who_need_help_web/live/request_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Confirm your email and ensure your account is active before publishing." msgstr "" @@ -566,125 +560,99 @@ msgstr "" msgid "Confirmation instructions" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:18 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:24 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:52 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:58 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:15 #, elixir-autogen, elixir-format msgid "Confirming..." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:238 -#, elixir-autogen, elixir-format -msgid "Could not complete moderation: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/show.ex:317 -#: lib/who_need_help_web/live/category_proposal_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:523 -#, elixir-autogen, elixir-format -msgid "Could not complete the action: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:103 -#, elixir-autogen, elixir-format -msgid "Could not publish the activity: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/request_live/new.ex:142 -#, elixir-autogen, elixir-format -msgid "Could not publish the request: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/profile_live.ex:87 -#, elixir-autogen, elixir-format -msgid "Could not unblock: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/index.ex:159 +#: lib/who_need_help_web/live/activity_live/index.ex:229 #, elixir-autogen, elixir-format msgid "Create a clear plan in a public place." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:119 +#: lib/who_need_help_web/live/activity_live/index.ex:188 #: lib/who_need_help_web/live/activity_live/new.ex:11 #, elixir-autogen, elixir-format msgid "Create activity" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:60 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 #, elixir-autogen, elixir-format msgid "Create an account" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/new.ex:115 #, elixir-autogen, elixir-format msgid "Create an activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:521 +#: lib/who_need_help_web/live/moderation_live.ex:525 #, elixir-autogen, elixir-format msgid "Create category" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:129 +#: lib/who_need_help_web/live/request_live/index.ex:203 #, elixir-autogen, elixir-format msgid "Create urgent request" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:58 #, elixir-autogen, elixir-format msgid "Creating account..." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:644 +#: lib/who_need_help_web/live/activity_live/show.ex:663 #, elixir-autogen, elixir-format msgid "Dangerous plan" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:244 -#: lib/who_need_help_web/live/request_live/show.ex:973 +#: lib/who_need_help_web/live/moderation_live.ex:248 +#: lib/who_need_help_web/live/request_live/show.ex:1086 #, elixir-autogen, elixir-format msgid "Dangerous request" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:573 +#: lib/who_need_help_web/live/moderation_live.ex:577 #, elixir-autogen, elixir-format msgid "Decision" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:519 +#: lib/who_need_help_web/live/moderation_live.ex:523 #, elixir-autogen, elixir-format msgid "Decision note" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:289 +#: lib/who_need_help_web/live/moderation_live.ex:293 #, elixir-autogen, elixir-format msgid "Decisions and access to reported chat evidence are written to the audit log." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:562 +#: lib/who_need_help_web/live/activity_live/show.ex:581 #, elixir-autogen, elixir-format msgid "Decline" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:161 +#: lib/who_need_help_web/live/profile_live.ex:164 #, elixir-autogen, elixir-format msgid "Default location visibility" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:225 +#: lib/who_need_help_web/live/activity_live/new.ex:227 #, elixir-autogen, elixir-format msgid "Describe the public plan, approximate duration, and who might enjoy it." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:256 -#: lib/who_need_help_web/live/moderation_live.ex:362 -#: lib/who_need_help_web/live/moderation_live.ex:448 +#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:366 +#: lib/who_need_help_web/live/moderation_live.ex:452 #, elixir-autogen, elixir-format msgid "Dismissed" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:35 -#: lib/who_need_help_web/live/profile_live.ex:146 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34 +#: lib/who_need_help_web/live/profile_live.ex:149 #, elixir-autogen, elixir-format msgid "Display name" msgstr "" @@ -699,25 +667,26 @@ msgstr "" msgid "Don't have an account?" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:704 +#: lib/who_need_help_web/live/request_live/show.ex:815 #, elixir-autogen, elixir-format msgid "Double-blind review" msgstr "" #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:25 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:38 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:56 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:71 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89 #: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15 #, elixir-autogen, elixir-format msgid "Email" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:65 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:89 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:96 #, elixir-autogen, elixir-format msgid "Email change link is invalid or it has expired." msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:84 #, elixir-autogen, elixir-format msgid "Email changed successfully." msgstr "" @@ -727,22 +696,22 @@ msgstr "" msgid "Email confirmation and social links are not identity guarantees. Manually attached social links are marked unverified. Check the person's history, unique interactions, handover evidence, and revealed reviews, and meet only where you feel safe." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:153 +#: lib/who_need_help_web/live/profile_live.ex:156 #, elixir-autogen, elixir-format msgid "English" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:502 +#: lib/who_need_help_web/live/moderation_live.ex:506 #, elixir-autogen, elixir-format msgid "English description" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:491 +#: lib/who_need_help_web/live/moderation_live.ex:495 #, elixir-autogen, elixir-format msgid "English name" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:881 +#: lib/who_need_help_web/live/request_live/show.ex:992 #, elixir-autogen, elixir-format msgid "Enter handover code" msgstr "" @@ -752,51 +721,51 @@ msgstr "" msgid "Exact address after matching" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:270 +#: lib/who_need_help_web/live/activity_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Exact coordinates are shown only to participants you approve." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:165 +#: lib/who_need_help_web/live/profile_live.ex:168 #, elixir-autogen, elixir-format msgid "Exact for active match" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:314 -#: lib/who_need_help_web/live/request_live/show.ex:543 +#: lib/who_need_help_web/live/request_live/new.ex:316 +#: lib/who_need_help_web/live/request_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Exact only for active match" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:166 -#: lib/who_need_help_web/live/request_live/show.ex:544 +#: lib/who_need_help_web/live/profile_live.ex:169 +#: lib/who_need_help_web/live/request_live/show.ex:642 #, elixir-autogen, elixir-format msgid "Exact publicly" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/new.ex:317 #, elixir-autogen, elixir-format msgid "Exact publicly — explicit consent" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:602 +#: lib/who_need_help_web/live/request_live/show.ex:710 #, elixir-autogen, elixir-format msgid "Expires:" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:272 +#: lib/who_need_help_web/live/request_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Explain what is already arranged and what the volunteer needs to do." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:110 +#: lib/who_need_help_web/live/activity_live/index.ex:179 #, elixir-autogen, elixir-format msgid "Find people to join" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:645 -#: lib/who_need_help_web/live/moderation_live.ex:246 -#: lib/who_need_help_web/live/request_live/show.ex:975 +#: lib/who_need_help_web/live/activity_live/show.ex:664 +#: lib/who_need_help_web/live/moderation_live.ex:250 +#: lib/who_need_help_web/live/request_live/show.ex:1088 #, elixir-autogen, elixir-format msgid "Fraud" msgstr "" @@ -806,39 +775,39 @@ msgstr "" msgid "Free help and thanks" msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:51 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:62 #, elixir-autogen, elixir-format msgid "GitHub profile verified." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:250 +#: lib/who_need_help_web/live/profile_live.ex:253 #, elixir-autogen, elixir-format msgid "GitHub verification is unavailable until the operator configures its OAuth credentials. No manually entered link is treated as verified." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:864 +#: lib/who_need_help_web/live/request_live/show.ex:975 #, elixir-autogen, elixir-format msgid "HANDOVER CODE FOR THE HELPER" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:319 +#: lib/who_need_help_web/live/profile_live.ex:322 #, elixir-autogen, elixir-format msgid "Handle (optional)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:122 +#: lib/who_need_help_web/live/request_live/show.ex:147 #, elixir-autogen, elixir-format msgid "Handover code verified." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:643 -#: lib/who_need_help_web/live/moderation_live.ex:245 -#: lib/who_need_help_web/live/request_live/show.ex:974 +#: lib/who_need_help_web/live/activity_live/show.ex:662 +#: lib/who_need_help_web/live/moderation_live.ex:249 +#: lib/who_need_help_web/live/request_live/show.ex:1087 #, elixir-autogen, elixir-format msgid "Harassment" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:110 +#: lib/who_need_help_web/live/request_live/show.ex:135 #, elixir-autogen, elixir-format msgid "Help is in progress." msgstr "" @@ -853,12 +822,12 @@ msgstr "" msgid "Helper" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:767 +#: lib/who_need_help_web/live/request_live/show.ex:878 #, elixir-autogen, elixir-format msgid "Helper: %{name}" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:64 +#: lib/who_need_help_web/components/layouts.ex:176 #, elixir-autogen, elixir-format msgid "Helpers" msgstr "" @@ -883,26 +852,26 @@ msgstr "" msgid "Hi %{email},\n\nYou can log into your account by visiting the URL below:\n\n%{url}\n\nIf you didn't request this email, please ignore this." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:257 -#: lib/who_need_help_web/live/profile_live.ex:163 -#: lib/who_need_help_web/live/request_live/new.ex:313 -#: lib/who_need_help_web/live/request_live/show.ex:542 +#: lib/who_need_help_web/live/activity_live/new.ex:259 +#: lib/who_need_help_web/live/profile_live.ex:166 +#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/show.ex:640 #, elixir-autogen, elixir-format msgid "Hidden" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:346 +#: lib/who_need_help_web/live/moderation_live.ex:350 #, elixir-autogen, elixir-format msgid "Hide activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:332 -#: lib/who_need_help_web/live/moderation_live.ex:344 +#: lib/who_need_help_web/live/moderation_live.ex:336 +#: lib/who_need_help_web/live/moderation_live.ex:348 #, elixir-autogen, elixir-format msgid "Hide reason" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:334 +#: lib/who_need_help_web/live/moderation_live.ex:338 #, elixir-autogen, elixir-format msgid "Hide request" msgstr "" @@ -912,33 +881,33 @@ msgstr "" msgid "Human decisions retained" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48 #, elixir-autogen, elixir-format msgid "I am 18 or older and accept the safety rules" msgstr "" #: lib/who_need_help_web/controllers/page_html/home.html.heex:56 -#: lib/who_need_help_web/live/request_live/show.ex:751 +#: lib/who_need_help_web/live/request_live/show.ex:862 #, elixir-autogen, elixir-format msgid "I can help" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:124 +#: lib/who_need_help_web/live/request_live/new.ex:127 #, elixir-autogen, elixir-format msgid "I confirm there is no immediate danger, people and vehicles are away from active traffic or emergency services have already been contacted, and the requested task is lawful." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:118 +#: lib/who_need_help_web/live/request_live/new.ex:121 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, medical advice request, controlled substance, or request to buy medicine on my behalf." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:130 +#: lib/who_need_help_web/live/request_live/new.ex:133 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, there is no immediate danger, and the requested task is lawful." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:292 +#: lib/who_need_help_web/live/activity_live/new.ex:298 #, elixir-autogen, elixir-format msgid "I will approve participants individually, keep sensitive details in the approved group, and use an appropriate public first meeting point." msgstr "" @@ -948,93 +917,88 @@ msgstr "" msgid "Identity and reputation" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:66 +#: lib/who_need_help_web/controllers/user_session_controller.ex:89 #, elixir-autogen, elixir-format msgid "If your email is in our system, you will receive instructions for logging in shortly." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:647 -#: lib/who_need_help_web/live/moderation_live.ex:249 -#: lib/who_need_help_web/live/request_live/show.ex:978 +#: lib/who_need_help_web/live/activity_live/show.ex:666 +#: lib/who_need_help_web/live/moderation_live.ex:253 +#: lib/who_need_help_web/live/request_live/show.ex:1091 #, elixir-autogen, elixir-format msgid "Impersonation" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:916 +#: lib/who_need_help_web/live/request_live/show.ex:1027 #, elixir-autogen, elixir-format msgid "In a browser this works only while the page remains open. The Android app shows a persistent notification and can continue after it is minimized. Raw coordinates are removed when sharing stops." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:106 -#: lib/who_need_help_web/live/request_live/index.ex:107 -#: lib/who_need_help_web/live/request_live/show.ex:536 +#: lib/who_need_help_web/live/request_live/index.ex:180 +#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/show.ex:634 #, elixir-autogen, elixir-format msgid "In progress" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:151 +#: lib/who_need_help_web/live/profile_live.ex:154 #, elixir-autogen, elixir-format msgid "Interface language" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:617 +#: lib/who_need_help_web/live/moderation_live.ex:621 #, elixir-autogen, elixir-format msgid "Internal note" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:47 +#: lib/who_need_help_web/controllers/user_session_controller.ex:124 #, elixir-autogen, elixir-format msgid "Invalid email or password" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:301 +#: lib/who_need_help_web/live/activity_live/show.ex:314 #, elixir-autogen, elixir-format msgid "Join requests are closed." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:361 +#: lib/who_need_help_web/live/activity_live/show.ex:380 #, elixir-autogen, elixir-format msgid "Join requests close" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:237 +#: lib/who_need_help_web/live/activity_live/new.ex:239 #, elixir-autogen, elixir-format msgid "Join requests close (UTC)" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:54 -#, elixir-autogen, elixir-format -msgid "Keep me logged in on this device" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:278 -#: lib/who_need_help_web/live/request_live/new.ex:336 +#: lib/who_need_help_web/live/activity_live/new.ex:282 +#: lib/who_need_help_web/live/request_live/new.ex:340 #, elixir-autogen, elixir-format msgid "Latitude" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:515 +#: lib/who_need_help_web/live/activity_live/show.ex:534 #, elixir-autogen, elixir-format msgid "Leave activity" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:914 +#: lib/who_need_help_web/live/request_live/show.ex:1025 #, elixir-autogen, elixir-format msgid "Live location" msgstr "" #: lib/who_need_help_web/controllers/page_html/safety.html.heex:51 -#: lib/who_need_help_web/live/activity_live/show.ex:484 +#: lib/who_need_help_web/live/activity_live/show.ex:503 #, elixir-autogen, elixir-format msgid "Location privacy" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:803 +#: lib/who_need_help_web/live/request_live/show.ex:914 #, elixir-autogen, elixir-format msgid "Location sharing is optional; missing evidence is not proof of abuse." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:310 +#: lib/who_need_help_web/live/request_live/new.ex:312 #, elixir-autogen, elixir-format msgid "Location visibility" msgstr "" @@ -1044,12 +1008,12 @@ msgstr "" msgid "Location-supported people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:609 +#: lib/who_need_help_web/live/request_live/show.ex:717 #, elixir-autogen, elixir-format msgid "Location:" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:69 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102 #, elixir-autogen, elixir-format msgid "Log in and stay logged in" msgstr "" @@ -1059,44 +1023,27 @@ msgstr "" msgid "Log in instructions" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:72 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105 #, elixir-autogen, elixir-format msgid "Log in only this time" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:45 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78 #, elixir-autogen, elixir-format msgid "Log in with email" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:60 -#, elixir-autogen, elixir-format -msgid "Log me in only this time" -msgstr "" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:102 +#: lib/who_need_help_web/controllers/user_session_controller.ex:117 #, elixir-autogen, elixir-format msgid "Logged out successfully." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:42 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:51 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:57 -#, elixir-autogen, elixir-format -msgid "Logging in..." -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:285 -#: lib/who_need_help_web/live/request_live/new.ex:343 +#: lib/who_need_help_web/live/activity_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/new.ex:347 #, elixir-autogen, elixir-format msgid "Longitude" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:95 -#, elixir-autogen, elixir-format -msgid "Magic link is invalid or it has expired." -msgstr "" - #: lib/who_need_help_web/controllers/feedback_html/show.html.heex:12 #, elixir-autogen, elixir-format msgid "Main Codex session ID" @@ -1107,12 +1054,12 @@ msgstr "" msgid "Manage your account email address and password settings" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:232 +#: lib/who_need_help_web/live/profile_live.ex:235 #, elixir-autogen, elixir-format msgid "Manually added links are public and marked unverified. A verified badge is only granted after completing a provider authorization flow." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:572 +#: lib/who_need_help_web/live/activity_live/show.ex:591 #, elixir-autogen, elixir-format msgid "Mark completed" msgstr "" @@ -1122,14 +1069,14 @@ msgstr "" msgid "Match and coordinate" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:104 -#: lib/who_need_help_web/live/request_live/index.ex:105 -#: lib/who_need_help_web/live/request_live/show.ex:534 +#: lib/who_need_help_web/live/request_live/index.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/show.ex:632 #, elixir-autogen, elixir-format msgid "Matched" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:110 +#: lib/who_need_help_web/live/request_live/new.ex:113 #, elixir-autogen, elixir-format msgid "Medicine is ready at the pharmacy" msgstr "" @@ -1144,7 +1091,7 @@ msgstr "" msgid "Medicine pickup" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:154 +#: lib/who_need_help_web/live/request_live/new.ex:156 #, elixir-autogen, elixir-format msgid "Medicine pickup remains the first priority. Roadside categories use the same moderated, extensible request flow with details specific to each type of help." msgstr "" @@ -1154,32 +1101,32 @@ msgstr "" msgid "Medicine requests" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:126 +#: lib/who_need_help_web/live/activity_live/index.ex:195 #, elixir-autogen, elixir-format msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:367 +#: lib/who_need_help_web/live/activity_live/show.ex:386 #, elixir-autogen, elixir-format msgid "Meeting area" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:548 +#: lib/who_need_help_web/live/moderation_live.ex:552 #, elixir-autogen, elixir-format msgid "Merge" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:542 +#: lib/who_need_help_web/live/moderation_live.ex:546 #, elixir-autogen, elixir-format msgid "Merge into" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:547 +#: lib/who_need_help_web/live/moderation_live.ex:551 #, elixir-autogen, elixir-format msgid "Merge note" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:464 +#: lib/who_need_help_web/live/activity_live/show.ex:483 #, elixir-autogen, elixir-format msgid "Message the approved group" msgstr "" @@ -1189,41 +1136,41 @@ msgstr "" msgid "Mode" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:73 +#: lib/who_need_help_web/components/layouts.ex:181 #: lib/who_need_help_web/live/moderation_live.ex:9 -#: lib/who_need_help_web/live/moderation_live.ex:287 +#: lib/who_need_help_web/live/moderation_live.ex:291 #, elixir-autogen, elixir-format msgid "Moderation" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:267 -#: lib/who_need_help_web/live/moderation_live.ex:633 +#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:637 #, elixir-autogen, elixir-format msgid "Moderator" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:226 -#: lib/who_need_help_web/user_auth.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/user_auth.ex:306 #, elixir-autogen, elixir-format msgid "Moderator access is required." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:122 +#: lib/who_need_help_web/live/request_live/index.ex:196 #, elixir-autogen, elixir-format msgid "NEARBY MUTUAL AID" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:64 +#: lib/who_need_help_web/live/request_live/index.ex:97 #, elixir-autogen, elixir-format msgid "Nearby help" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:113 +#: lib/who_need_help_web/live/request_live/new.ex:116 #, elixir-autogen, elixir-format msgid "Need roadside help in a safe location" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:301 +#: lib/who_need_help_web/live/profile_live.ex:304 #, elixir-autogen, elixir-format msgid "Network" msgstr "" @@ -1233,10 +1180,10 @@ msgstr "" msgid "New password" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:205 -#: lib/who_need_help_web/live/activity_live/show.ex:283 -#: lib/who_need_help_web/live/request_live/new.ex:253 -#: lib/who_need_help_web/live/request_live/show.ex:472 +#: lib/who_need_help_web/live/activity_live/new.ex:207 +#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/request_live/new.ex:255 +#: lib/who_need_help_web/live/request_live/show.ex:563 #, elixir-autogen, elixir-format msgid "No" msgstr "" @@ -1246,28 +1193,28 @@ msgstr "" msgid "No completed help yet." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:432 -#: lib/who_need_help_web/live/request_live/show.ex:663 +#: lib/who_need_help_web/live/activity_live/show.ex:451 +#: lib/who_need_help_web/live/request_live/show.ex:771 #, elixir-autogen, elixir-format msgid "No messages yet." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:157 +#: lib/who_need_help_web/live/activity_live/index.ex:227 #, elixir-autogen, elixir-format msgid "No open activities yet" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/index.ex:254 #, elixir-autogen, elixir-format msgid "No open requests yet" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:460 #, elixir-autogen, elixir-format msgid "No open signals." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:567 +#: lib/who_need_help_web/live/activity_live/show.ex:586 #, elixir-autogen, elixir-format msgid "No pending join requests." msgstr "" @@ -1277,17 +1224,17 @@ msgstr "" msgid "No proposals yet." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:372 +#: lib/who_need_help_web/live/moderation_live.ex:376 #, elixir-autogen, elixir-format msgid "No reports." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:258 +#: lib/who_need_help_web/live/profile_live.ex:261 #, elixir-autogen, elixir-format msgid "No social links added." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:209 +#: lib/who_need_help_web/live/profile_live.ex:212 #, elixir-autogen, elixir-format msgid "None revealed yet." msgstr "" @@ -1302,22 +1249,22 @@ msgstr "" msgid "Not for emergencies" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:94 -#: lib/who_need_help_web/live/request_live/index.ex:95 #: lib/who_need_help_web/live/request_live/index.ex:168 -#: lib/who_need_help_web/live/request_live/new.ex:291 -#: lib/who_need_help_web/live/request_live/show.ex:525 -#: lib/who_need_help_web/live/request_live/show.ex:526 +#: lib/who_need_help_web/live/request_live/index.ex:169 +#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/new.ex:293 +#: lib/who_need_help_web/live/request_live/show.ex:623 +#: lib/who_need_help_web/live/request_live/show.ex:624 #, elixir-autogen, elixir-format msgid "Now" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:108 +#: lib/who_need_help_web/live/activity_live/index.ex:177 #, elixir-autogen, elixir-format msgid "OPTIONAL SOCIAL PLANS" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:125 +#: lib/who_need_help_web/live/request_live/index.ex:199 #, elixir-autogen, elixir-format msgid "Only approximate areas are shown before a safe match." msgstr "" @@ -1327,27 +1274,27 @@ msgstr "" msgid "Only request pickup of a legal item already reserved or paid for. Do not request medical advice, a prescription, controlled substances, cash advances, repackaging, or a purchase on your behalf. Follow pharmacy rules and local law." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:390 +#: lib/who_need_help_web/live/moderation_live.ex:394 #, elixir-autogen, elixir-format msgid "Only the conversation linked to this report is displayed." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:418 +#: lib/who_need_help_web/live/activity_live/show.ex:437 #, elixir-autogen, elixir-format msgid "Only the organizer and approved participants can read or send these messages." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:97 -#: lib/who_need_help_web/live/activity_live/show.ex:319 -#: lib/who_need_help_web/live/moderation_live.ex:253 -#: lib/who_need_help_web/live/request_live/index.ex:102 -#: lib/who_need_help_web/live/request_live/index.ex:103 -#: lib/who_need_help_web/live/request_live/show.ex:533 +#: lib/who_need_help_web/live/activity_live/index.ex:166 +#: lib/who_need_help_web/live/activity_live/show.ex:338 +#: lib/who_need_help_web/live/moderation_live.ex:257 +#: lib/who_need_help_web/live/request_live/index.ex:176 +#: lib/who_need_help_web/live/request_live/index.ex:177 +#: lib/who_need_help_web/live/request_live/show.ex:631 #, elixir-autogen, elixir-format msgid "Open" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:420 +#: lib/who_need_help_web/live/moderation_live.ex:424 #, elixir-autogen, elixir-format msgid "Open abuse signals" msgstr "" @@ -1357,40 +1304,40 @@ msgstr "" msgid "Open proposals" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:171 +#: lib/who_need_help_web/live/profile_live.ex:174 #, elixir-autogen, elixir-format msgid "Optional external thank-you link" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:900 +#: lib/who_need_help_web/live/request_live/show.ex:1011 #, elixir-autogen, elixir-format msgid "Optional thank-you link" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:371 +#: lib/who_need_help_web/live/activity_live/show.ex:390 #, elixir-autogen, elixir-format msgid "Organizer" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:160 +#: lib/who_need_help_web/live/activity_live/show.ex:155 #, elixir-autogen, elixir-format msgid "Organizer blocked. Their activities and messages are hidden." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:520 +#: lib/who_need_help_web/live/activity_live/show.ex:539 #, elixir-autogen, elixir-format msgid "Organizer controls" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:380 +#: lib/who_need_help_web/live/activity_live/show.ex:399 #, elixir-autogen, elixir-format msgid "Organizer social links" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:648 -#: lib/who_need_help_web/live/moderation_live.ex:250 -#: lib/who_need_help_web/live/profile_live.ex:308 -#: lib/who_need_help_web/live/request_live/show.ex:979 +#: lib/who_need_help_web/live/activity_live/show.ex:667 +#: lib/who_need_help_web/live/moderation_live.ex:254 +#: lib/who_need_help_web/live/profile_live.ex:311 +#: lib/who_need_help_web/live/request_live/show.ex:1092 #, elixir-autogen, elixir-format msgid "Other" msgstr "" @@ -1400,57 +1347,57 @@ msgstr "" msgid "Parent (optional)" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:440 +#: lib/who_need_help_web/live/activity_live/show.ex:459 #, elixir-autogen, elixir-format msgid "Participant" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:64 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97 #, elixir-autogen, elixir-format msgid "Password" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:47 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 #, elixir-autogen, elixir-format msgid "Password updated successfully." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:278 +#: lib/who_need_help_web/live/request_live/new.ex:280 #, elixir-autogen, elixir-format msgid "Pickup instructions (optional)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:578 +#: lib/who_need_help_web/live/request_live/show.ex:686 #, elixir-autogen, elixir-format msgid "Pickup notes" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:223 +#: lib/who_need_help_web/live/activity_live/new.ex:225 #, elixir-autogen, elixir-format msgid "Plan and expectations" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:402 +#: lib/who_need_help_web/live/activity_live/show.ex:421 #, elixir-autogen, elixir-format msgid "Plan details" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:520 +#: lib/who_need_help_web/live/request_live/show.ex:619 #, elixir-autogen, elixir-format msgid "Please check the entered data." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:235 +#: lib/who_need_help_web/live/moderation_live.ex:240 #, elixir-autogen, elixir-format msgid "Please check the submitted fields." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:314 +#: lib/who_need_help_web/live/activity_live/show.ex:334 #, elixir-autogen, elixir-format msgid "Please check the submitted message." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:305 +#: lib/who_need_help_web/live/request_live/new.ex:307 #, elixir-autogen, elixir-format msgid "Podil, near the central pharmacy" msgstr "" @@ -1460,12 +1407,13 @@ msgstr "" msgid "Post a clear request" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:45 +#: lib/who_need_help_web/components/layouts.ex:55 +#: lib/who_need_help_web/components/layouts.ex:87 #, elixir-autogen, elixir-format msgid "Primary navigation" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:647 +#: lib/who_need_help_web/live/request_live/show.ex:755 #, elixir-autogen, elixir-format msgid "Private match chat" msgstr "" @@ -1480,8 +1428,8 @@ msgstr "" msgid "Profile updated." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:976 +#: lib/who_need_help_web/live/moderation_live.ex:251 +#: lib/who_need_help_web/live/request_live/show.ex:1089 #, elixir-autogen, elixir-format msgid "Prohibited item" msgstr "" @@ -1516,27 +1464,27 @@ msgstr "" msgid "Public location can be hidden, approximate, or explicitly exact. Live tracking is optional. A browser shares only while its page is open; Android shows a persistent foreground-service notification while sharing. The current exact point is deleted when sharing stops, a match ends, or either participant blocks the other; derived movement and proximity evidence may remain." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:254 +#: lib/who_need_help_web/live/activity_live/new.ex:256 #, elixir-autogen, elixir-format msgid "Public map visibility" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:314 +#: lib/who_need_help_web/live/profile_live.ex:317 #, elixir-autogen, elixir-format msgid "Public profile URL" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:182 +#: lib/who_need_help_web/live/profile_live.ex:185 #, elixir-autogen, elixir-format msgid "Public reputation" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:115 +#: lib/who_need_help_web/live/activity_live/new.ex:117 #, elixir-autogen, elixir-format msgid "Publish a clear public plan. Join requests stay pending until you approve them." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:301 +#: lib/who_need_help_web/live/activity_live/new.ex:310 #, elixir-autogen, elixir-format msgid "Publish activity" msgstr "" @@ -1546,13 +1494,13 @@ msgstr "" msgid "Publish proposal" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:355 +#: lib/who_need_help_web/live/request_live/new.ex:364 #, elixir-autogen, elixir-format msgid "Publish request" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:299 -#: lib/who_need_help_web/live/request_live/new.ex:353 +#: lib/who_need_help_web/live/activity_live/new.ex:308 +#: lib/who_need_help_web/live/request_live/new.ex:362 #, elixir-autogen, elixir-format msgid "Publishing…" msgstr "" @@ -1562,7 +1510,7 @@ msgstr "" msgid "READ BEFORE USING THE SERVICE" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:286 +#: lib/who_need_help_web/live/moderation_live.ex:290 #, elixir-autogen, elixir-format msgid "RESTRICTED WORKSPACE" msgstr "" @@ -1578,17 +1526,17 @@ msgid "Ranking prioritizes unique people helped with optional location-supported msgstr "" #: lib/who_need_help_web/live/leaderboard_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:712 +#: lib/who_need_help_web/live/request_live/show.ex:823 #, elixir-autogen, elixir-format msgid "Rating" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40 #, elixir-autogen, elixir-format msgid "Read the" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:641 +#: lib/who_need_help_web/live/activity_live/show.ex:660 #, elixir-autogen, elixir-format msgid "Reason" msgstr "" @@ -1598,38 +1546,38 @@ msgstr "" msgid "Register for an account" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:531 +#: lib/who_need_help_web/live/moderation_live.ex:535 #, elixir-autogen, elixir-format msgid "Reject" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:530 +#: lib/who_need_help_web/live/moderation_live.ex:534 #, elixir-autogen, elixir-format msgid "Rejection note" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:292 +#: lib/who_need_help_web/live/profile_live.ex:295 #, elixir-autogen, elixir-format msgid "Remove" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:449 +#: lib/who_need_help_web/live/activity_live/show.ex:468 #, elixir-autogen, elixir-format msgid "Report" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:681 +#: lib/who_need_help_web/live/request_live/show.ex:789 #, elixir-autogen, elixir-format msgid "Report message" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:971 +#: lib/who_need_help_web/live/request_live/show.ex:1084 #, elixir-autogen, elixir-format msgid "Report reason" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:128 -#: lib/who_need_help_web/live/request_live/show.ex:231 +#: lib/who_need_help_web/live/activity_live/show.ex:123 +#: lib/who_need_help_web/live/request_live/show.ex:261 #, elixir-autogen, elixir-format msgid "Report sent to moderators." msgstr "" @@ -1639,27 +1587,27 @@ msgstr "" msgid "Report updated." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:386 +#: lib/who_need_help_web/live/moderation_live.ex:390 #, elixir-autogen, elixir-format msgid "Reported evidence" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:293 +#: lib/who_need_help_web/live/moderation_live.ex:297 #, elixir-autogen, elixir-format msgid "Reports" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:940 +#: lib/who_need_help_web/live/request_live/show.ex:1053 #, elixir-autogen, elixir-format msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:102 +#: lib/who_need_help_web/live/request_live/show.ex:127 #, elixir-autogen, elixir-format msgid "Request cancelled." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:299 +#: lib/who_need_help_web/live/request_live/new.ex:301 #, elixir-autogen, elixir-format msgid "Request expires (UTC)" msgstr "" @@ -1674,82 +1622,82 @@ msgstr "" msgid "Request restored." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:502 +#: lib/who_need_help_web/live/activity_live/show.ex:521 #, elixir-autogen, elixir-format msgid "Request to join" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:617 +#: lib/who_need_help_web/live/request_live/show.ex:725 #, elixir-autogen, elixir-format msgid "Requester links:" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:598 +#: lib/who_need_help_web/live/request_live/show.ex:706 #, elixir-autogen, elixir-format msgid "Requester:" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:632 +#: lib/who_need_help_web/live/request_live/show.ex:740 #, elixir-autogen, elixir-format msgid "Requester: %{count} completed" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:367 +#: lib/who_need_help_web/live/moderation_live.ex:371 #, elixir-autogen, elixir-format msgid "Resolution note" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:255 -#: lib/who_need_help_web/live/moderation_live.ex:361 +#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:365 #, elixir-autogen, elixir-format msgid "Resolved" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:262 -#: lib/who_need_help_web/live/moderation_live.ex:611 +#: lib/who_need_help_web/live/moderation_live.ex:266 +#: lib/who_need_help_web/live/moderation_live.ex:615 #, elixir-autogen, elixir-format msgid "Restricted" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:451 +#: lib/who_need_help_web/live/moderation_live.ex:455 #, elixir-autogen, elixir-format msgid "Review note" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:203 +#: lib/who_need_help_web/live/request_live/show.ex:233 #, elixir-autogen, elixir-format msgid "Review saved. It appears after both participants review." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:257 -#: lib/who_need_help_web/live/moderation_live.ex:447 +#: lib/who_need_help_web/live/moderation_live.ex:261 +#: lib/who_need_help_web/live/moderation_live.ex:451 #, elixir-autogen, elixir-format msgid "Reviewed" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:254 -#: lib/who_need_help_web/live/moderation_live.ex:360 +#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:364 #, elixir-autogen, elixir-format msgid "Reviewing" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:571 +#: lib/who_need_help_web/live/moderation_live.ex:575 #, elixir-autogen, elixir-format msgid "Role" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:510 +#: lib/who_need_help_web/live/moderation_live.ex:514 #, elixir-autogen, elixir-format msgid "Russian description (optional)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:498 +#: lib/who_need_help_web/live/moderation_live.ex:502 #, elixir-autogen, elixir-format msgid "Russian name (optional)" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:616 -#: lib/who_need_help_web/live/request_live/show.ex:938 +#: lib/who_need_help_web/live/activity_live/show.ex:635 +#: lib/who_need_help_web/live/request_live/show.ex:1051 #, elixir-autogen, elixir-format msgid "Safety controls" msgstr "" @@ -1759,15 +1707,15 @@ msgstr "" msgid "Safety policy, category approval, account moderation, public launch, and every production infrastructure change remain human-controlled." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:104 +#: lib/who_need_help_web/components/layouts.ex:145 #: lib/who_need_help_web/controllers/page_html/safety.html.heex:6 #, elixir-autogen, elixir-format msgid "Safety rules" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:369 -#: lib/who_need_help_web/live/moderation_live.ex:452 -#: lib/who_need_help_web/live/moderation_live.ex:619 +#: lib/who_need_help_web/live/moderation_live.ex:373 +#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:623 #, elixir-autogen, elixir-format msgid "Save" msgstr "" @@ -1777,29 +1725,29 @@ msgstr "" msgid "Save Password" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:177 +#: lib/who_need_help_web/live/profile_live.ex:180 #, elixir-autogen, elixir-format msgid "Save profile" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:98 -#: lib/who_need_help_web/live/request_live/index.ex:99 -#: lib/who_need_help_web/live/request_live/index.ex:170 -#: lib/who_need_help_web/live/request_live/new.ex:293 -#: lib/who_need_help_web/live/request_live/show.ex:529 -#: lib/who_need_help_web/live/request_live/show.ex:530 +#: lib/who_need_help_web/live/request_live/index.ex:172 +#: lib/who_need_help_web/live/request_live/index.ex:173 +#: lib/who_need_help_web/live/request_live/index.ex:245 +#: lib/who_need_help_web/live/request_live/new.ex:295 +#: lib/who_need_help_web/live/request_live/show.ex:627 +#: lib/who_need_help_web/live/request_live/show.ex:628 #, elixir-autogen, elixir-format msgid "Scheduled" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:467 -#: lib/who_need_help_web/live/request_live/show.ex:696 +#: lib/who_need_help_web/live/activity_live/show.ex:486 +#: lib/who_need_help_web/live/request_live/show.ex:806 #, elixir-autogen, elixir-format msgid "Send" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:657 -#: lib/who_need_help_web/live/request_live/show.ex:988 +#: lib/who_need_help_web/live/activity_live/show.ex:676 +#: lib/who_need_help_web/live/request_live/show.ex:1101 #, elixir-autogen, elixir-format msgid "Send report" msgstr "" @@ -1809,28 +1757,23 @@ msgstr "" msgid "Set CODEX_SESSION_ID in the deployment environment before the submission recording." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:926 +#: lib/who_need_help_web/live/request_live/show.ex:1038 #, elixir-autogen, elixir-format msgid "Share location" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:280 +#: lib/who_need_help_web/live/request_live/new.ex:282 #, elixir-autogen, elixir-format msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:501 -#, elixir-autogen, elixir-format -msgid "Shared live location" -msgstr "" - -#: lib/who_need_help_web/live/profile_live.ex:147 +#: lib/who_need_help_web/live/profile_live.ex:150 #, elixir-autogen, elixir-format msgid "Short bio" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:217 -#: lib/who_need_help_web/live/request_live/new.ex:264 +#: lib/who_need_help_web/live/activity_live/new.ex:219 +#: lib/who_need_help_web/live/request_live/new.ex:266 #, elixir-autogen, elixir-format msgid "Short title" msgstr "" @@ -1845,12 +1788,12 @@ msgstr "" msgid "Signal updated." msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:52 +#: lib/who_need_help_web/components/layouts/root.html.heex:27 #, elixir-autogen, elixir-format msgid "Skip to main content" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:490 +#: lib/who_need_help_web/live/moderation_live.ex:494 #, elixir-autogen, elixir-format msgid "Slug" msgstr "" @@ -1871,12 +1814,12 @@ msgstr "" msgid "Social link added as unverified." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:67 +#: lib/who_need_help_web/live/profile_live.ex:74 #, elixir-autogen, elixir-format msgid "Social link was not found." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:230 +#: lib/who_need_help_web/live/profile_live.ex:233 #, elixir-autogen, elixir-format msgid "Social links" msgstr "" @@ -1886,54 +1829,54 @@ msgstr "" msgid "Social verification could not be started." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:77 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:88 #, elixir-autogen, elixir-format msgid "Social verification failed. Please retry." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:58 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 #, elixir-autogen, elixir-format msgid "Social verification session expired. Please retry." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:65 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:76 #, elixir-autogen, elixir-format msgid "Social verification session is invalid." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:646 -#: lib/who_need_help_web/live/moderation_live.ex:248 -#: lib/who_need_help_web/live/request_live/show.ex:977 +#: lib/who_need_help_web/live/activity_live/show.ex:665 +#: lib/who_need_help_web/live/moderation_live.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:1090 #, elixir-autogen, elixir-format msgid "Spam" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:829 +#: lib/who_need_help_web/live/request_live/show.ex:940 #, elixir-autogen, elixir-format msgid "Start helping" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:355 +#: lib/who_need_help_web/live/activity_live/show.ex:374 #, elixir-autogen, elixir-format msgid "Starts" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:232 +#: lib/who_need_help_web/live/activity_live/new.ex:234 #, elixir-autogen, elixir-format msgid "Starts at (UTC)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:572 +#: lib/who_need_help_web/live/moderation_live.ex:576 #, elixir-autogen, elixir-format msgid "Status" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:933 +#: lib/who_need_help_web/live/request_live/show.ex:1046 #, elixir-autogen, elixir-format msgid "Stop and delete position" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:515 +#: lib/who_need_help_web/live/moderation_live.ex:519 #, elixir-autogen, elixir-format msgid "Structured fields JSON" msgstr "" @@ -1943,34 +1886,34 @@ msgstr "" msgid "Structured fields must be a valid JSON object." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:726 +#: lib/who_need_help_web/live/request_live/show.ex:837 #, elixir-autogen, elixir-format msgid "Submit review" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:263 -#: lib/who_need_help_web/live/moderation_live.ex:612 +#: lib/who_need_help_web/live/moderation_live.ex:267 +#: lib/who_need_help_web/live/moderation_live.ex:616 #, elixir-autogen, elixir-format msgid "Suspended" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:509 +#: lib/who_need_help_web/live/request_live/show.ex:604 #, elixir-autogen, elixir-format msgid "That handover code is not correct." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:145 -#: lib/who_need_help_web/live/request_live/show.ex:248 +#: lib/who_need_help_web/live/activity_live/show.ex:140 +#: lib/who_need_help_web/live/request_live/show.ex:278 #, elixir-autogen, elixir-format msgid "That message cannot be reported from this view." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:80 #, elixir-autogen, elixir-format msgid "That social account is already linked." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:513 +#: lib/who_need_help_web/live/request_live/show.ex:608 #, elixir-autogen, elixir-format msgid "That step is not available in the current state." msgstr "" @@ -1985,27 +1928,27 @@ msgstr "" msgid "That verification provider is not supported." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:299 +#: lib/who_need_help_web/live/activity_live/show.ex:312 #, elixir-autogen, elixir-format msgid "The approved group has reached its capacity." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:180 +#: lib/who_need_help_web/live/request_live/show.ex:208 #, elixir-autogen, elixir-format msgid "The browser could not share your location." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:233 +#: lib/who_need_help_web/live/moderation_live.ex:237 #, elixir-autogen, elixir-format msgid "The last administrator cannot demote themselves." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:31 +#: lib/who_need_help_web/controllers/user_session_controller.ex:37 #, elixir-autogen, elixir-format msgid "The link is invalid or it has expired." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:507 +#: lib/who_need_help_web/live/activity_live/show.ex:526 #, elixir-autogen, elixir-format msgid "The organizer has not approved your request yet." msgstr "" @@ -2015,58 +1958,58 @@ msgstr "" msgid "The pharmacy closes soon and I cannot leave home. The item is already paid for." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:175 +#: lib/who_need_help_web/live/profile_live.ex:178 #, elixir-autogen, elixir-format msgid "The platform never handles, requires, splits, or guarantees a payment." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:489 +#: lib/who_need_help_web/live/activity_live/show.ex:508 #, elixir-autogen, elixir-format msgid "The public map is approximate or hidden. Exact coordinates appear only after approval." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:629 +#: lib/who_need_help_web/live/activity_live/show.ex:648 #, elixir-autogen, elixir-format msgid "The report is scoped to the selected group message." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:963 +#: lib/who_need_help_web/live/request_live/show.ex:1076 #, elixir-autogen, elixir-format msgid "The report will be scoped to the selected chat message." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:302 +#: lib/who_need_help_web/live/activity_live/show.ex:315 #, elixir-autogen, elixir-format msgid "This action is unavailable." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:309 +#: lib/who_need_help_web/live/activity_live/show.ex:322 #, elixir-autogen, elixir-format msgid "This activity is no longer open." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:307 +#: lib/who_need_help_web/live/activity_live/show.ex:320 #, elixir-autogen, elixir-format msgid "This activity state has already changed." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:518 +#: lib/who_need_help_web/live/request_live/show.ex:613 #, elixir-autogen, elixir-format msgid "This interaction is blocked." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:136 -#: lib/who_need_help_web/live/request_live/show.ex:557 +#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/request_live/show.ex:665 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:162 +#: lib/who_need_help_web/live/request_live/new.ex:164 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger. Never include prescription details, payment card data, access codes, or other sensitive information." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:227 +#: lib/who_need_help_web/live/moderation_live.ex:231 #, elixir-autogen, elixir-format msgid "This proposal has already been reviewed." msgstr "" @@ -2076,65 +2019,62 @@ msgstr "" msgid "This proposal is already closed." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:406 +#: lib/who_need_help_web/live/moderation_live.ex:410 #, elixir-autogen, elixir-format msgid "This report has no linked conversation." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:905 +#: lib/who_need_help_web/live/request_live/show.ex:1016 #, elixir-autogen, elixir-format msgid "This request already has a helper." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:52 +#: lib/who_need_help_web/live/request_live/show.ex:74 +#: lib/who_need_help_web/live/request_live/show.ex:615 #, elixir-autogen, elixir-format msgid "This request is no longer available." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:37 +#: lib/who_need_help_web/live/request_live/show.ex:59 #, elixir-autogen, elixir-format msgid "This request is unavailable." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:66 -#, elixir-autogen, elixir-format -msgid "Tip: If you prefer passwords, you can enable them in the user settings." -msgstr "" - #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:27 #, elixir-autogen, elixir-format msgid "To see sent emails, visit" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:96 -#: lib/who_need_help_web/live/request_live/index.ex:97 -#: lib/who_need_help_web/live/request_live/index.ex:169 -#: lib/who_need_help_web/live/request_live/new.ex:292 -#: lib/who_need_help_web/live/request_live/show.ex:527 -#: lib/who_need_help_web/live/request_live/show.ex:528 +#: lib/who_need_help_web/live/request_live/index.ex:170 +#: lib/who_need_help_web/live/request_live/index.ex:171 +#: lib/who_need_help_web/live/request_live/index.ex:244 +#: lib/who_need_help_web/live/request_live/new.ex:294 +#: lib/who_need_help_web/live/request_live/show.ex:625 +#: lib/who_need_help_web/live/request_live/show.ex:626 #, elixir-autogen, elixir-format msgid "Today" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:100 -#: lib/who_need_help_web/live/activity_live/show.ex:312 +#: lib/who_need_help_web/live/activity_live/new.ex:103 +#: lib/who_need_help_web/live/activity_live/show.ex:332 #: lib/who_need_help_web/live/category_proposal_live.ex:54 -#: lib/who_need_help_web/live/request_live/show.ex:519 +#: lib/who_need_help_web/live/profile_live.ex:59 +#: lib/who_need_help_web/live/request_live/show.ex:618 #, elixir-autogen, elixir-format msgid "Too many actions in the configured time window." msgstr "" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:45 +#: lib/who_need_help_web/controllers/user_registration_controller.ex:49 #, elixir-autogen, elixir-format msgid "Too many registration attempts in the configured time window." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:139 +#: lib/who_need_help_web/live/request_live/new.ex:142 #, elixir-autogen, elixir-format msgid "Too many requests in the configured time window." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:79 +#: lib/who_need_help_web/controllers/user_session_controller.ex:102 #, elixir-autogen, elixir-format msgid "Too many sign-in emails in the configured time window." msgstr "" @@ -2149,27 +2089,27 @@ msgstr "" msgid "Total" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:244 +#: lib/who_need_help_web/live/activity_live/new.ex:246 #, elixir-autogen, elixir-format msgid "Total group capacity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:506 +#: lib/who_need_help_web/live/moderation_live.ex:510 #, elixir-autogen, elixir-format msgid "Ukrainian description (optional)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:494 +#: lib/who_need_help_web/live/moderation_live.ex:498 #, elixir-autogen, elixir-format msgid "Ukrainian name (optional)" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:342 +#: lib/who_need_help_web/live/profile_live.ex:345 #, elixir-autogen, elixir-format msgid "Unblock" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:956 +#: lib/who_need_help_web/live/request_live/show.ex:1069 #, elixir-autogen, elixir-format msgid "Unblock this user" msgstr "" @@ -2184,8 +2124,8 @@ msgstr "" msgid "Update email instructions" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:165 -#: lib/who_need_help_web/live/request_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/index.ex:240 +#: lib/who_need_help_web/live/request_live/new.ex:291 #, elixir-autogen, elixir-format msgid "Urgency" msgstr "" @@ -2196,23 +2136,23 @@ msgstr "" msgid "Urgent help" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:121 +#: lib/who_need_help_web/live/activity_live/new.ex:123 #, elixir-autogen, elixir-format msgid "Use a public first meeting point. Do not publish a home address, access code, travel document, or other sensitive information." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:194 +#: lib/who_need_help_web/components/layouts.ex:349 #, elixir-autogen, elixir-format msgid "Use dark theme" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:185 +#: lib/who_need_help_web/components/layouts.ex:340 #, elixir-autogen, elixir-format msgid "Use light theme" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:267 -#: lib/who_need_help_web/live/request_live/new.ex:325 +#: lib/who_need_help_web/live/activity_live/new.ex:271 +#: lib/who_need_help_web/live/request_live/new.ex:329 #, elixir-autogen, elixir-format msgid "Use my current foreground location" msgstr "" @@ -2222,24 +2162,24 @@ msgstr "" msgid "Use private chat and optional live location sharing." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:176 +#: lib/who_need_help_web/components/layouts.ex:331 #, elixir-autogen, elixir-format msgid "Use system theme" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:266 -#: lib/who_need_help_web/live/moderation_live.ex:570 -#: lib/who_need_help_web/live/moderation_live.ex:632 +#: lib/who_need_help_web/live/moderation_live.ex:270 +#: lib/who_need_help_web/live/moderation_live.ex:574 +#: lib/who_need_help_web/live/moderation_live.ex:636 #, elixir-autogen, elixir-format msgid "User" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:271 +#: lib/who_need_help_web/live/request_live/show.ex:307 #, elixir-autogen, elixir-format msgid "User blocked. Their requests and new messages are hidden." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:19 +#: lib/who_need_help_web/controllers/user_session_controller.ex:25 #, elixir-autogen, elixir-format msgid "User confirmed successfully." msgstr "" @@ -2254,8 +2194,8 @@ msgstr "" msgid "User status updated." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:80 -#: lib/who_need_help_web/live/request_live/show.ex:285 +#: lib/who_need_help_web/live/profile_live.ex:87 +#: lib/who_need_help_web/live/request_live/show.ex:321 #, elixir-autogen, elixir-format msgid "User unblocked." msgstr "" @@ -2265,12 +2205,12 @@ msgstr "" msgid "Verified people" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:242 +#: lib/who_need_help_web/live/profile_live.ex:245 #, elixir-autogen, elixir-format msgid "Verify GitHub" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:885 +#: lib/who_need_help_web/live/request_live/show.ex:996 #, elixir-autogen, elixir-format msgid "Verify handover" msgstr "" @@ -2280,28 +2220,23 @@ msgstr "" msgid "Verify the handover" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:323 +#: lib/who_need_help_web/live/moderation_live.ex:327 #, elixir-autogen, elixir-format msgid "View scoped evidence" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:207 +#: lib/who_need_help_web/live/profile_live.ex:210 #, elixir-autogen, elixir-format msgid "Visible reviews" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:755 +#: lib/who_need_help_web/live/request_live/show.ex:866 #, elixir-autogen, elixir-format msgid "Waiting for a nearby volunteer." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:4 -#, elixir-autogen, elixir-format -msgid "Welcome %{email}" -msgstr "" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:20 -#: lib/who_need_help_web/controllers/user_session_controller.ex:40 +#: lib/who_need_help_web/controllers/user_session_controller.ex:26 +#: lib/who_need_help_web/controllers/user_session_controller.ex:51 #, elixir-autogen, elixir-format msgid "Welcome back!" msgstr "" @@ -2311,8 +2246,8 @@ msgstr "" msgid "What Codex contributed" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:654 -#: lib/who_need_help_web/live/request_live/show.ex:985 +#: lib/who_need_help_web/live/activity_live/show.ex:673 +#: lib/who_need_help_web/live/request_live/show.ex:1098 #, elixir-autogen, elixir-format msgid "What happened?" msgstr "" @@ -2322,7 +2257,7 @@ msgstr "" msgid "What help category is missing?" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:270 +#: lib/who_need_help_web/live/request_live/new.ex:272 #, elixir-autogen, elixir-format msgid "What help do you need?" msgstr "" @@ -2337,12 +2272,12 @@ msgstr "" msgid "Who Need Help coordinates voluntary help between adults. It cannot verify every person, request, item, route, or outcome and cannot guarantee safety." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:40 +#: lib/who_need_help_web/components/layouts.ex:43 #, elixir-autogen, elixir-format msgid "Who Need Help home" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:54 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53 #, elixir-autogen, elixir-format msgid "Who Need Help is not an emergency or medical service. Medicine requests are limited to pickup of legal items already purchased or reserved." msgstr "" @@ -2352,7 +2287,7 @@ msgstr "" msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:123 +#: lib/who_need_help_web/live/request_live/index.ex:197 #, elixir-autogen, elixir-format msgid "Who needs help?" msgstr "" @@ -2362,46 +2297,46 @@ msgstr "" msgid "Why is this useful?" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:846 +#: lib/who_need_help_web/live/request_live/show.ex:957 #, elixir-autogen, elixir-format msgid "Withdraw from this request" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:693 +#: lib/who_need_help_web/live/request_live/show.ex:802 #, elixir-autogen, elixir-format msgid "Write a safe coordination message…" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:202 -#: lib/who_need_help_web/live/activity_live/show.ex:281 -#: lib/who_need_help_web/live/request_live/new.ex:250 -#: lib/who_need_help_web/live/request_live/show.ex:470 +#: lib/who_need_help_web/live/activity_live/new.ex:204 +#: lib/who_need_help_web/live/activity_live/show.ex:294 +#: lib/who_need_help_web/live/request_live/new.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:561 #, elixir-autogen, elixir-format msgid "Yes" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/activity_live/show.ex:309 #, elixir-autogen, elixir-format msgid "You already have a join request for this activity." msgstr "" -#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#: lib/who_need_help_web/live/category_proposal_live.ex:58 #, elixir-autogen, elixir-format msgid "You already voted for this proposal." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:487 +#: lib/who_need_help_web/live/activity_live/show.ex:506 #, elixir-autogen, elixir-format msgid "You are approved, so the map can show the exact meeting coordinates." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:92 +#: lib/who_need_help_web/live/request_live/show.ex:117 #, elixir-autogen, elixir-format msgid "You are matched. Use chat to coordinate safely." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:304 -#: lib/who_need_help_web/live/request_live/show.ex:510 +#: lib/who_need_help_web/live/activity_live/show.ex:317 +#: lib/who_need_help_web/live/request_live/show.ex:605 #, elixir-autogen, elixir-format msgid "You are not allowed to do that." msgstr "" @@ -2411,44 +2346,44 @@ msgstr "" msgid "You are running the local mail adapter." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:328 +#: lib/who_need_help_web/live/request_live/new.ex:332 #, elixir-autogen, elixir-format msgid "You can also enter coordinates manually for local testing." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/index.ex:256 #, elixir-autogen, elixir-format msgid "You can be the first person to ask the community." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:507 +#: lib/who_need_help_web/live/request_live/show.ex:602 #, elixir-autogen, elixir-format msgid "You cannot accept your own request." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:303 +#: lib/who_need_help_web/live/activity_live/show.ex:316 #, elixir-autogen, elixir-format msgid "You cannot report your own content." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/live/moderation_live.ex:234 #, elixir-autogen, elixir-format msgid "You cannot restrict your own moderator account." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:330 +#: lib/who_need_help_web/live/profile_live.ex:333 #, elixir-autogen, elixir-format msgid "You have not blocked anyone." msgstr "" -#: lib/who_need_help_web/user_auth.ex:208 -#: lib/who_need_help_web/user_auth.ex:240 -#: lib/who_need_help_web/user_auth.ex:270 +#: lib/who_need_help_web/user_auth.ex:256 +#: lib/who_need_help_web/user_auth.ex:288 +#: lib/who_need_help_web/user_auth.ex:318 #, elixir-autogen, elixir-format msgid "You must log in to access this page." msgstr "" -#: lib/who_need_help_web/user_auth.ex:178 +#: lib/who_need_help_web/user_auth.ex:226 #, elixir-autogen, elixir-format msgid "You must re-authenticate to access this page." msgstr "" @@ -2458,22 +2393,22 @@ msgstr "" msgid "You need to reauthenticate to perform sensitive actions on your account." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:117 +#: lib/who_need_help_web/live/request_live/show.ex:142 #, elixir-autogen, elixir-format msgid "You withdrew from this request." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:221 +#: lib/who_need_help_web/live/activity_live/index.ex:292 #, elixir-autogen, elixir-format msgid "Your activities" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:113 +#: lib/who_need_help_web/live/request_live/show.ex:138 #, elixir-autogen, elixir-format msgid "Your confirmation was saved." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:139 +#: lib/who_need_help_web/live/profile_live.ex:142 #, elixir-autogen, elixir-format msgid "Your profile" msgstr "" @@ -2483,52 +2418,52 @@ msgstr "" msgid "Your request is now visible to nearby helpers." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:266 +#: lib/who_need_help_web/live/request_live/index.ex:342 #, elixir-autogen, elixir-format msgid "Your requests" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:706 +#: lib/who_need_help_web/live/request_live/show.ex:817 #, elixir-autogen, elixir-format msgid "Your review is revealed only after both participants submit." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/index.ex:319 #, elixir-autogen, elixir-format msgid "Your trust summary" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:307 +#: lib/who_need_help_web/live/moderation_live.ex:311 #, elixir-autogen, elixir-format msgid "activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:310 +#: lib/who_need_help_web/live/moderation_live.ex:314 #, elixir-autogen, elixir-format msgid "activity message" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 #, elixir-autogen, elixir-format msgid "before confirming." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:190 +#: lib/who_need_help_web/live/activity_live/index.ex:261 #: lib/who_need_help_web/live/category_proposal_live.ex:147 -#: lib/who_need_help_web/live/moderation_live.ex:313 -#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/moderation_live.ex:317 +#: lib/who_need_help_web/live/request_live/index.ex:286 #, elixir-autogen, elixir-format msgid "by %{name}" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:186 -#: lib/who_need_help_web/live/request_live/index.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:772 +#: lib/who_need_help_web/live/profile_live.ex:189 +#: lib/who_need_help_web/live/request_live/index.ex:323 +#: lib/who_need_help_web/live/request_live/show.ex:883 #, elixir-autogen, elixir-format msgid "completed" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:215 +#: lib/who_need_help_web/live/request_live/index.ex:291 #, elixir-autogen, elixir-format msgid "expires %{time}" msgstr "" @@ -2538,59 +2473,59 @@ msgstr "" msgid "for an account now." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:799 +#: lib/who_need_help_web/live/request_live/show.ex:910 #, elixir-autogen, elixir-format msgid "helper movement supported" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:800 +#: lib/who_need_help_web/live/request_live/show.ex:911 #, elixir-autogen, elixir-format msgid "no movement evidence" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:791 +#: lib/who_need_help_web/live/request_live/show.ex:902 #, elixir-autogen, elixir-format msgid "no proximity evidence" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82 #, elixir-autogen, elixir-format msgid "or" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:586 +#: lib/who_need_help_web/live/activity_live/show.ex:605 #, elixir-autogen, elixir-format msgid "organizer" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:648 +#: lib/who_need_help_web/live/request_live/show.ex:756 #, elixir-autogen, elixir-format msgid "participants only" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:252 -#: lib/who_need_help_web/live/request_live/show.ex:776 +#: lib/who_need_help_web/live/request_live/index.ex:328 +#: lib/who_need_help_web/live/request_live/show.ex:887 #, elixir-autogen, elixir-format msgid "people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:790 +#: lib/who_need_help_web/live/request_live/show.ex:901 #, elixir-autogen, elixir-format msgid "proximity supported" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:201 -#: lib/who_need_help_web/live/request_live/show.ex:780 +#: lib/who_need_help_web/live/profile_live.ex:204 +#: lib/who_need_help_web/live/request_live/show.ex:891 #, elixir-autogen, elixir-format msgid "rating" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:640 +#: lib/who_need_help_web/live/request_live/show.ex:748 #, elixir-autogen, elixir-format msgid "rating %{rating}" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 #, elixir-autogen, elixir-format msgid "safety rules" msgstr "" @@ -2605,17 +2540,17 @@ msgstr "" msgid "to your account now." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:191 +#: lib/who_need_help_web/live/profile_live.ex:194 #, elixir-autogen, elixir-format msgid "unique people" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:395 -#: lib/who_need_help_web/live/activity_live/show.ex:546 -#: lib/who_need_help_web/live/activity_live/show.ex:605 -#: lib/who_need_help_web/live/profile_live.ex:274 -#: lib/who_need_help_web/live/request_live/show.ex:627 -#: lib/who_need_help_web/live/request_live/show.ex:821 +#: lib/who_need_help_web/live/activity_live/show.ex:414 +#: lib/who_need_help_web/live/activity_live/show.ex:565 +#: lib/who_need_help_web/live/activity_live/show.ex:624 +#: lib/who_need_help_web/live/profile_live.ex:277 +#: lib/who_need_help_web/live/request_live/show.ex:735 +#: lib/who_need_help_web/live/request_live/show.ex:932 #, elixir-autogen, elixir-format msgid "unverified" msgstr "" @@ -2625,34 +2560,34 @@ msgstr "" msgid "urgent" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:394 -#: lib/who_need_help_web/live/activity_live/show.ex:545 -#: lib/who_need_help_web/live/activity_live/show.ex:604 -#: lib/who_need_help_web/live/profile_live.ex:273 -#: lib/who_need_help_web/live/request_live/index.ex:257 -#: lib/who_need_help_web/live/request_live/show.ex:626 -#: lib/who_need_help_web/live/request_live/show.ex:820 +#: lib/who_need_help_web/live/activity_live/show.ex:413 +#: lib/who_need_help_web/live/activity_live/show.ex:564 +#: lib/who_need_help_web/live/activity_live/show.ex:623 +#: lib/who_need_help_web/live/profile_live.ex:276 +#: lib/who_need_help_web/live/request_live/index.ex:333 +#: lib/who_need_help_web/live/request_live/show.ex:734 +#: lib/who_need_help_web/live/request_live/show.ex:931 #, elixir-autogen, elixir-format msgid "verified" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:196 +#: lib/who_need_help_web/live/profile_live.ex:199 #, elixir-autogen, elixir-format msgid "verified handovers" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:552 +#: lib/who_need_help_web/live/request_live/show.ex:660 #, elixir-autogen, elixir-format msgid "← All requests" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:111 -#: lib/who_need_help_web/live/activity_live/show.ex:329 +#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/show.ex:348 #, elixir-autogen, elixir-format msgid "← Back to activities" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:150 +#: lib/who_need_help_web/live/request_live/new.ex:152 #, elixir-autogen, elixir-format msgid "← Back to requests" msgstr "" @@ -2662,83 +2597,221 @@ msgstr "" msgid "← Requests" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:262 #, elixir-autogen, elixir-format msgid "Approved" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:112 -#: lib/who_need_help_web/live/request_live/index.ex:113 +#: lib/who_need_help_web/live/request_live/index.ex:186 +#: lib/who_need_help_web/live/request_live/index.ex:187 #, elixir-autogen, elixir-format msgid "Expired" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:275 +#: lib/who_need_help_web/live/moderation_live.ex:279 #, elixir-autogen, elixir-format msgid "Handover without location evidence" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:277 +#: lib/who_need_help_web/live/moderation_live.ex:281 #, elixir-autogen, elixir-format msgid "Location without movement" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:279 +#: lib/who_need_help_web/live/moderation_live.ex:283 #, elixir-autogen, elixir-format msgid "Manual review" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:264 #, elixir-autogen, elixir-format msgid "Merged" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:278 +#: lib/who_need_help_web/live/moderation_live.ex:282 #, elixir-autogen, elixir-format msgid "Reciprocal rating ring" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:263 #, elixir-autogen, elixir-format msgid "Rejected" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:276 #, elixir-autogen, elixir-format msgid "Repeated participant pair" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:275 #, elixir-autogen, elixir-format msgid "Unusual action rate" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:429 -#: lib/who_need_help_web/live/request_live/show.ex:660 +#: lib/who_need_help_web/live/activity_live/show.ex:448 +#: lib/who_need_help_web/live/request_live/show.ex:768 #, elixir-autogen, elixir-format msgid "Load earlier messages" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:202 -#: lib/who_need_help_web/live/activity_live/index.ex:238 +#: lib/who_need_help_web/live/activity_live/index.ex:273 +#: lib/who_need_help_web/live/activity_live/index.ex:310 #: lib/who_need_help_web/live/category_proposal_live.ex:162 #: lib/who_need_help_web/live/leaderboard_live.ex:87 -#: lib/who_need_help_web/live/moderation_live.ex:380 -#: lib/who_need_help_web/live/moderation_live.ex:415 -#: lib/who_need_help_web/live/moderation_live.ex:560 -#: lib/who_need_help_web/live/moderation_live.ex:652 -#: lib/who_need_help_web/live/profile_live.ex:221 -#: lib/who_need_help_web/live/profile_live.ex:351 -#: lib/who_need_help_web/live/request_live/index.ex:227 -#: lib/who_need_help_web/live/request_live/index.ex:283 +#: lib/who_need_help_web/live/moderation_live.ex:384 +#: lib/who_need_help_web/live/moderation_live.ex:419 +#: lib/who_need_help_web/live/moderation_live.ex:564 +#: lib/who_need_help_web/live/moderation_live.ex:656 +#: lib/who_need_help_web/live/profile_live.ex:224 +#: lib/who_need_help_web/live/profile_live.ex:354 +#: lib/who_need_help_web/live/request_live/index.ex:303 +#: lib/who_need_help_web/live/request_live/index.ex:360 #, elixir-autogen, elixir-format msgid "Load more" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:213 -#: lib/who_need_help_web/live/activity_live/show.ex:479 -#: lib/who_need_help_web/live/request_live/index.ex:238 -#: lib/who_need_help_web/live/request_live/show.ex:738 +#: lib/who_need_help_web/live/activity_live/index.ex:284 +#: lib/who_need_help_web/live/activity_live/show.ex:498 +#: lib/who_need_help_web/live/request_live/index.ex:314 +#: lib/who_need_help_web/live/request_live/show.ex:849 #, elixir-autogen, elixir-format msgid "The map is unavailable in this browser. Request details remain usable." msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:4 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Confirm email change" +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:5 +#, elixir-autogen, elixir-format +msgid "Confirm the new email address for your account." +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:242 +#, elixir-autogen, elixir-format +msgid "Could not complete moderation. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:336 +#: lib/who_need_help_web/live/category_proposal_live.ex:60 +#: lib/who_need_help_web/live/request_live/show.ex:621 +#, elixir-autogen, elixir-format +msgid "Could not complete the action. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/new.ex:105 +#, elixir-autogen, elixir-format +msgid "Could not publish the activity. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/request_live/new.ex:144 +#, elixir-autogen, elixir-format +msgid "Could not publish the request. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/profile_live.ex:91 +#, elixir-autogen, elixir-format +msgid "Could not unblock this user. Please try again." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:22 +#, elixir-autogen, elixir-format +msgid "JavaScript is required to confirm this protected email link." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:594 +#, elixir-autogen, elixir-format +msgid "Last update: %{time}" +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:617 +#, elixir-autogen, elixir-format +msgid "Live location sharing is not active." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:592 +#, elixir-autogen, elixir-format +msgid "Shared location" +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:329 +#, elixir-autogen, elixir-format +msgid "The organizer cannot leave the activity." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:326 +#, elixir-autogen, elixir-format +msgid "The organizer is already attending." +msgstr "" + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "The registration form is invalid." +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:239 +#, elixir-autogen, elixir-format +msgid "The selected record is no longer available." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "The settings form is invalid." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_controller.ex:111 +#, elixir-autogen, elixir-format +msgid "The sign-in form is invalid." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:323 +#, elixir-autogen, elixir-format +msgid "This activity is no longer available." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:616 +#, elixir-autogen, elixir-format +msgid "This match is no longer active." +msgstr "" + +#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#, elixir-autogen, elixir-format +msgid "This proposal is no longer available." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:614 +#, elixir-autogen, elixir-format +msgid "This request has expired." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "Too many sign-in attempts in the configured time window." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:46 +#, elixir-autogen, elixir-format +msgid "Your secure email link is ready. Continue to sign in." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:128 +#, elixir-autogen, elixir-format +msgid "The confirmation email could not be sent. Please try again." +msgstr "" + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:109 +#, elixir-autogen, elixir-format +msgid "If this address can be registered or signed in, login instructions will arrive shortly." +msgstr "" + +#: lib/who_need_help_web/live/profile_live.ex:62 +#, elixir-autogen, elixir-format +msgid "This account cannot perform that action." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:40 +#, elixir-autogen, elixir-format +msgid "Too many email-change requests in the configured time window." +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index cbd1efe..bc2379b 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -11,30 +11,32 @@ msgstr "" "Language: en\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: lib/who_need_help_web/components/layouts/root.html.heex:67 +#: lib/who_need_help_web/components/layouts.ex:111 +#: lib/who_need_help_web/components/layouts.ex:257 #, elixir-autogen, elixir-format msgid "Account settings" msgstr "" -#: lib/who_need_help_web/components/core_components.ex:375 +#: lib/who_need_help_web/components/core_components.ex:393 #, elixir-autogen, elixir-format msgid "Actions" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:83 +#: lib/who_need_help_web/components/layouts.ex:70 +#: lib/who_need_help_web/components/layouts.ex:119 #: lib/who_need_help_web/controllers/page_html/home.html.heex:21 #: lib/who_need_help_web/live/request_live/new.ex:22 #, elixir-autogen, elixir-format msgid "Ask for help" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:140 -#: lib/who_need_help_web/components/layouts.ex:155 +#: lib/who_need_help_web/components/layouts.ex:295 +#: lib/who_need_help_web/components/layouts.ex:310 #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:59 +#: lib/who_need_help_web/components/layouts.ex:171 #, elixir-autogen, elixir-format msgid "Categories" msgstr "" @@ -54,33 +56,34 @@ msgstr "" msgid "Join the community" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:89 -#: lib/who_need_help_web/components/layouts/root.html.heex:77 +#: lib/who_need_help_web/components/layouts.ex:78 +#: lib/who_need_help_web/components/layouts.ex:128 #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:9 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:45 #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:5 #, elixir-autogen, elixir-format msgid "Log in" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:70 +#: lib/who_need_help_web/components/layouts.ex:114 +#: lib/who_need_help_web/components/layouts.ex:260 #, elixir-autogen, elixir-format msgid "Log out" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:78 -#: lib/who_need_help_web/components/layouts/root.html.heex:64 +#: lib/who_need_help_web/components/layouts.ex:108 +#: lib/who_need_help_web/components/layouts.ex:254 #: lib/who_need_help_web/live/profile_live.ex:16 #, elixir-autogen, elixir-format msgid "Profile" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:74 +#: lib/who_need_help_web/components/layouts.ex:75 +#: lib/who_need_help_web/components/layouts.ex:125 #, elixir-autogen, elixir-format msgid "Register" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:49 +#: lib/who_need_help_web/components/layouts.ex:161 #, elixir-autogen, elixir-format msgid "Requests" msgstr "" @@ -90,12 +93,12 @@ msgstr "" msgid "See nearby requests" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:147 +#: lib/who_need_help_web/components/layouts.ex:302 #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:132 +#: lib/who_need_help_web/components/layouts.ex:287 #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "" @@ -105,99 +108,95 @@ msgstr "" msgid "close" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:344 +#: lib/who_need_help_web/live/activity_live/show.ex:363 #, elixir-autogen, elixir-format msgid "%{approved}/%{capacity} approved" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:481 +#: lib/who_need_help_web/live/moderation_live.ex:485 #, elixir-autogen, elixir-format msgid "%{count} community votes" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:637 +#: lib/who_need_help_web/live/request_live/show.ex:745 #, elixir-autogen, elixir-format msgid "%{count} unique people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:718 +#: lib/who_need_help_web/live/request_live/show.ex:829 #, elixir-autogen, elixir-format msgid "1 — unsafe" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:717 +#: lib/who_need_help_web/live/request_live/show.ex:828 #, elixir-autogen, elixir-format msgid "2 — poor" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:716 +#: lib/who_need_help_web/live/request_live/show.ex:827 #, elixir-autogen, elixir-format msgid "3 — okay" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:715 +#: lib/who_need_help_web/live/request_live/show.ex:826 #, elixir-autogen, elixir-format msgid "4 — good" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:714 +#: lib/who_need_help_web/live/request_live/show.ex:825 #, elixir-autogen, elixir-format msgid "5 — excellent" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:31 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:118 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:433 +#: lib/who_need_help_web/live/moderation_live.ex:437 #, elixir-autogen, elixir-format msgid "A signal requests human review; optional tracking absence is not proof of abuse." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:748 +#: lib/who_need_help_web/live/request_live/show.ex:859 #, elixir-autogen, elixir-format msgid "Accept only if you can complete this safely and for free." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:535 +#: lib/who_need_help_web/live/request_live/show.ex:633 #, elixir-autogen, elixir-format msgid "Accepted" msgstr "" #: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:4 -#, elixir-autogen, elixir-format, fuzzy +#, elixir-autogen, elixir-format msgid "Account Settings" msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:54 +#: lib/who_need_help_web/components/layouts.ex:63 +#: lib/who_need_help_web/components/layouts.ex:192 #, elixir-autogen, elixir-format msgid "Account and language" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:565 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/moderation_live.ex:569 +#, elixir-autogen, elixir-format msgid "Accounts" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:744 +#: lib/who_need_help_web/live/request_live/show.ex:855 #, elixir-autogen, elixir-format msgid "Action center" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:261 -#: lib/who_need_help_web/live/moderation_live.ex:610 +#: lib/who_need_help_web/live/moderation_live.ex:265 +#: lib/who_need_help_web/live/moderation_live.ex:614 #, elixir-autogen, elixir-format msgid "Active" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:502 -#, elixir-autogen, elixir-format -msgid "Active match only" -msgstr "" - -#: lib/who_need_help_web/components/layouts.ex:54 -#: lib/who_need_help_web/live/activity_live/index.ex:68 +#: lib/who_need_help_web/components/layouts.ex:166 +#: lib/who_need_help_web/live/activity_live/index.ex:118 #, elixir-autogen, elixir-format msgid "Activities" msgstr "" @@ -207,7 +206,7 @@ msgstr "" msgid "Activities are optional plans, not urgent help, and do not affect helper reputation. Meet in a suitable public place first. Organizers approve every participant; exact coordinates and group chat are limited to approved participants. Share route and safety plans for hikes, and never publish home addresses, access codes, or travel documents." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:161 +#: lib/who_need_help_web/live/activity_live/new.ex:163 #, elixir-autogen, elixir-format msgid "Activity details" msgstr "" @@ -217,8 +216,8 @@ msgstr "" msgid "Activity hidden." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:16 -#: lib/who_need_help_web/live/activity_live/show.ex:182 +#: lib/who_need_help_web/live/activity_live/show.ex:18 +#: lib/who_need_help_web/live/activity_live/show.ex:177 #, elixir-autogen, elixir-format msgid "Activity is unavailable." msgstr "" @@ -233,30 +232,30 @@ msgstr "" msgid "Activity restored." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:140 -#: lib/who_need_help_web/live/activity_live/new.ex:137 +#: lib/who_need_help_web/live/activity_live/index.ex:210 +#: lib/who_need_help_web/live/activity_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Activity type" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:322 +#: lib/who_need_help_web/live/profile_live.ex:325 #, elixir-autogen, elixir-format msgid "Add unverified link" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:268 -#: lib/who_need_help_web/live/moderation_live.ex:634 +#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:638 #, elixir-autogen, elixir-format msgid "Administrator" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:142 +#: lib/who_need_help_web/live/activity_live/index.ex:212 #, elixir-autogen, elixir-format msgid "All activities" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:152 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/index.ex:227 +#, elixir-autogen, elixir-format msgid "All categories" msgstr "" @@ -265,71 +264,66 @@ msgstr "" msgid "Already registered?" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:29 -#, elixir-autogen, elixir-format -msgid "An email was sent to %{email}, please access it to confirm your account." -msgstr "" - -#: lib/who_need_help_web/live/request_live/show.ex:508 +#: lib/who_need_help_web/live/request_live/show.ex:603 #, elixir-autogen, elixir-format msgid "Another helper already accepted this request." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:167 +#: lib/who_need_help_web/live/request_live/index.ex:242 #, elixir-autogen, elixir-format msgid "Any urgency" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:505 +#: lib/who_need_help_web/live/activity_live/show.ex:524 #, elixir-autogen, elixir-format msgid "Approval pending" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:555 +#: lib/who_need_help_web/live/activity_live/show.ex:574 #, elixir-autogen, elixir-format msgid "Approve" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:416 +#: lib/who_need_help_web/live/activity_live/show.ex:435 #, elixir-autogen, elixir-format msgid "Approved group chat" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:581 +#: lib/who_need_help_web/live/activity_live/show.ex:600 #, elixir-autogen, elixir-format msgid "Approved participants" msgstr "" #: lib/who_need_help_web/controllers/page_html/home.html.heex:51 -#: lib/who_need_help_web/live/activity_live/new.ex:256 -#: lib/who_need_help_web/live/request_live/new.ex:304 +#: lib/who_need_help_web/live/activity_live/new.ex:258 +#: lib/who_need_help_web/live/request_live/new.ex:306 #, elixir-autogen, elixir-format msgid "Approximate area" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Approximate public meeting area" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:164 -#: lib/who_need_help_web/live/request_live/show.ex:541 +#: lib/who_need_help_web/live/profile_live.ex:167 +#: lib/who_need_help_web/live/request_live/show.ex:639 #, elixir-autogen, elixir-format msgid "Approximate publicly" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:312 +#: lib/who_need_help_web/live/request_live/new.ex:314 #, elixir-autogen, elixir-format msgid "Approximate publicly (recommended)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:594 +#: lib/who_need_help_web/live/request_live/show.ex:702 #, elixir-autogen, elixir-format msgid "Area:" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:152 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/new.ex:154 +#, elixir-autogen, elixir-format msgid "Ask for urgent help" msgstr "" @@ -348,17 +342,17 @@ msgstr "" msgid "Block and report" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:622 +#: lib/who_need_help_web/live/activity_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Block organizer" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:949 +#: lib/who_need_help_web/live/request_live/show.ex:1062 #, elixir-autogen, elixir-format msgid "Block this user" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:328 +#: lib/who_need_help_web/live/profile_live.ex:331 #, elixir-autogen, elixir-format msgid "Blocked users" msgstr "" @@ -368,12 +362,12 @@ msgstr "" msgid "Both confirm and the helper enters a one-time code." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:115 +#: lib/who_need_help_web/live/request_live/new.ex:118 #, elixir-autogen, elixir-format msgid "Briefly describe the help you need" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:106 +#: lib/who_need_help_web/components/layouts.ex:147 #, elixir-autogen, elixir-format msgid "Build Week feedback" msgstr "" @@ -393,29 +387,29 @@ msgstr "" msgid "COMMUNITY-DRIVEN TAXONOMY" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:575 +#: lib/who_need_help_web/live/activity_live/show.ex:594 #, elixir-autogen, elixir-format msgid "Cancel activity" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:762 -#: lib/who_need_help_web/live/request_live/show.ex:856 +#: lib/who_need_help_web/live/request_live/show.ex:873 +#: lib/who_need_help_web/live/request_live/show.ex:967 #, elixir-autogen, elixir-format msgid "Cancel request" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:99 -#: lib/who_need_help_web/live/activity_live/show.ex:321 -#: lib/who_need_help_web/live/request_live/index.ex:110 -#: lib/who_need_help_web/live/request_live/index.ex:111 -#: lib/who_need_help_web/live/request_live/show.ex:538 +#: lib/who_need_help_web/live/activity_live/index.ex:168 +#: lib/who_need_help_web/live/activity_live/show.ex:340 +#: lib/who_need_help_web/live/request_live/index.ex:184 +#: lib/who_need_help_web/live/request_live/index.ex:185 +#: lib/who_need_help_web/live/request_live/show.ex:636 #, elixir-autogen, elixir-format msgid "Cancelled" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:150 -#: lib/who_need_help_web/live/request_live/new.ex:178 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/index.ex:225 +#: lib/who_need_help_web/live/request_live/new.ex:180 +#, elixir-autogen, elixir-format msgid "Category" msgstr "" @@ -424,14 +418,14 @@ msgstr "" msgid "Category created and proposal approved." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:209 -#: lib/who_need_help_web/live/request_live/show.ex:583 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/new.ex:211 +#: lib/who_need_help_web/live/request_live/show.ex:691 +#, elixir-autogen, elixir-format msgid "Category details" msgstr "" #: lib/who_need_help_web/live/category_proposal_live.ex:66 -#: lib/who_need_help_web/live/moderation_live.ex:462 +#: lib/who_need_help_web/live/moderation_live.ex:466 #, elixir-autogen, elixir-format msgid "Category proposals" msgstr "" @@ -441,7 +435,7 @@ msgstr "" msgid "Change Email" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:638 +#: lib/who_need_help_web/live/moderation_live.ex:642 #, elixir-autogen, elixir-format msgid "Change role" msgstr "" @@ -457,51 +451,51 @@ msgstr "" msgid "Choose a category, urgency, and safe location visibility." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:138 +#: lib/who_need_help_web/live/activity_live/new.ex:140 #, elixir-autogen, elixir-format msgid "Choose activity" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:179 +#: lib/who_need_help_web/live/request_live/new.ex:181 #, elixir-autogen, elixir-format msgid "Choose category" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:173 -#: lib/who_need_help_web/live/activity_live/new.ex:200 -#: lib/who_need_help_web/live/request_live/new.ex:221 -#: lib/who_need_help_web/live/request_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:175 +#: lib/who_need_help_web/live/activity_live/new.ex:202 +#: lib/who_need_help_web/live/request_live/new.ex:223 +#: lib/who_need_help_web/live/request_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Choose…" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:249 +#: lib/who_need_help_web/live/activity_live/new.ex:251 #, elixir-autogen, elixir-format msgid "City centre, near the main entrance" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:635 -#: lib/who_need_help_web/live/request_live/show.ex:965 +#: lib/who_need_help_web/live/activity_live/show.ex:654 +#: lib/who_need_help_web/live/request_live/show.ex:1078 #, elixir-autogen, elixir-format msgid "Clear" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:387 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/moderation_live.ex:391 +#, elixir-autogen, elixir-format msgid "Close" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:218 +#: lib/who_need_help_web/live/activity_live/new.ex:220 #, elixir-autogen, elixir-format msgid "Coffee and conversation" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:113 +#: lib/who_need_help_web/live/activity_live/index.ex:182 #, elixir-autogen, elixir-format msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:724 +#: lib/who_need_help_web/live/request_live/show.ex:835 #, elixir-autogen, elixir-format msgid "Comment (optional)" msgstr "" @@ -511,35 +505,35 @@ msgstr "" msgid "Community helpers" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:191 -#: lib/who_need_help_web/live/activity_live/show.ex:373 -#: lib/who_need_help_web/live/activity_live/show.ex:527 -#: lib/who_need_help_web/live/activity_live/show.ex:584 -#: lib/who_need_help_web/live/request_live/index.ex:211 +#: lib/who_need_help_web/live/activity_live/index.ex:262 +#: lib/who_need_help_web/live/activity_live/show.ex:392 +#: lib/who_need_help_web/live/activity_live/show.ex:546 +#: lib/who_need_help_web/live/activity_live/show.ex:603 +#: lib/who_need_help_web/live/request_live/index.ex:287 #, elixir-autogen, elixir-format msgid "Community member" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:98 -#: lib/who_need_help_web/live/activity_live/show.ex:320 -#: lib/who_need_help_web/live/request_live/index.ex:108 -#: lib/who_need_help_web/live/request_live/index.ex:109 -#: lib/who_need_help_web/live/request_live/show.ex:537 +#: lib/who_need_help_web/live/activity_live/index.ex:167 +#: lib/who_need_help_web/live/activity_live/show.ex:339 +#: lib/who_need_help_web/live/request_live/index.ex:182 +#: lib/who_need_help_web/live/request_live/index.ex:183 +#: lib/who_need_help_web/live/request_live/show.ex:635 #, elixir-autogen, elixir-format msgid "Completed" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:27 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:61 #, elixir-autogen, elixir-format msgid "Confirm and log in only this time" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:21 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:55 #, elixir-autogen, elixir-format msgid "Confirm and stay logged in" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:836 +#: lib/who_need_help_web/live/request_live/show.ex:947 #, elixir-autogen, elixir-format msgid "Confirm my part is complete" msgstr "" @@ -550,13 +544,13 @@ msgid "Confirm new password" msgstr "" #: lib/who_need_help_web/live/category_proposal_live.ex:51 -#: lib/who_need_help_web/live/request_live/show.ex:516 +#: lib/who_need_help_web/live/request_live/show.ex:611 #, elixir-autogen, elixir-format msgid "Confirm your account and ensure it is active first." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:97 -#: lib/who_need_help_web/live/request_live/new.ex:136 +#: lib/who_need_help_web/live/activity_live/new.ex:100 +#: lib/who_need_help_web/live/request_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Confirm your email and ensure your account is active before publishing." msgstr "" @@ -566,125 +560,99 @@ msgstr "" msgid "Confirmation instructions" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:18 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:24 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:52 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:58 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:15 #, elixir-autogen, elixir-format msgid "Confirming..." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:238 -#, elixir-autogen, elixir-format -msgid "Could not complete moderation: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/show.ex:317 -#: lib/who_need_help_web/live/category_proposal_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:523 -#, elixir-autogen, elixir-format -msgid "Could not complete the action: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:103 -#, elixir-autogen, elixir-format -msgid "Could not publish the activity: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/request_live/new.ex:142 -#, elixir-autogen, elixir-format -msgid "Could not publish the request: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/profile_live.ex:87 -#, elixir-autogen, elixir-format -msgid "Could not unblock: %{reason}" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/index.ex:159 +#: lib/who_need_help_web/live/activity_live/index.ex:229 #, elixir-autogen, elixir-format msgid "Create a clear plan in a public place." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:119 +#: lib/who_need_help_web/live/activity_live/index.ex:188 #: lib/who_need_help_web/live/activity_live/new.ex:11 #, elixir-autogen, elixir-format msgid "Create activity" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:60 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 #, elixir-autogen, elixir-format msgid "Create an account" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/new.ex:115 #, elixir-autogen, elixir-format msgid "Create an activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:521 +#: lib/who_need_help_web/live/moderation_live.ex:525 #, elixir-autogen, elixir-format msgid "Create category" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:129 +#: lib/who_need_help_web/live/request_live/index.ex:203 #, elixir-autogen, elixir-format msgid "Create urgent request" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:58 #, elixir-autogen, elixir-format msgid "Creating account..." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:644 +#: lib/who_need_help_web/live/activity_live/show.ex:663 #, elixir-autogen, elixir-format msgid "Dangerous plan" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:244 -#: lib/who_need_help_web/live/request_live/show.ex:973 +#: lib/who_need_help_web/live/moderation_live.ex:248 +#: lib/who_need_help_web/live/request_live/show.ex:1086 #, elixir-autogen, elixir-format msgid "Dangerous request" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:573 +#: lib/who_need_help_web/live/moderation_live.ex:577 #, elixir-autogen, elixir-format msgid "Decision" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:519 +#: lib/who_need_help_web/live/moderation_live.ex:523 #, elixir-autogen, elixir-format msgid "Decision note" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:289 +#: lib/who_need_help_web/live/moderation_live.ex:293 #, elixir-autogen, elixir-format msgid "Decisions and access to reported chat evidence are written to the audit log." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:562 +#: lib/who_need_help_web/live/activity_live/show.ex:581 #, elixir-autogen, elixir-format msgid "Decline" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:161 +#: lib/who_need_help_web/live/profile_live.ex:164 #, elixir-autogen, elixir-format msgid "Default location visibility" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:225 +#: lib/who_need_help_web/live/activity_live/new.ex:227 #, elixir-autogen, elixir-format msgid "Describe the public plan, approximate duration, and who might enjoy it." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:256 -#: lib/who_need_help_web/live/moderation_live.ex:362 -#: lib/who_need_help_web/live/moderation_live.ex:448 +#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:366 +#: lib/who_need_help_web/live/moderation_live.ex:452 #, elixir-autogen, elixir-format msgid "Dismissed" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:35 -#: lib/who_need_help_web/live/profile_live.ex:146 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34 +#: lib/who_need_help_web/live/profile_live.ex:149 #, elixir-autogen, elixir-format msgid "Display name" msgstr "" @@ -699,25 +667,26 @@ msgstr "" msgid "Don't have an account?" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:704 +#: lib/who_need_help_web/live/request_live/show.ex:815 #, elixir-autogen, elixir-format msgid "Double-blind review" msgstr "" #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:25 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:38 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:56 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:71 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89 #: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15 #, elixir-autogen, elixir-format msgid "Email" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:65 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:89 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:96 #, elixir-autogen, elixir-format msgid "Email change link is invalid or it has expired." msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:84 #, elixir-autogen, elixir-format msgid "Email changed successfully." msgstr "" @@ -727,22 +696,22 @@ msgstr "" msgid "Email confirmation and social links are not identity guarantees. Manually attached social links are marked unverified. Check the person's history, unique interactions, handover evidence, and revealed reviews, and meet only where you feel safe." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:153 +#: lib/who_need_help_web/live/profile_live.ex:156 #, elixir-autogen, elixir-format msgid "English" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:502 +#: lib/who_need_help_web/live/moderation_live.ex:506 #, elixir-autogen, elixir-format msgid "English description" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:491 +#: lib/who_need_help_web/live/moderation_live.ex:495 #, elixir-autogen, elixir-format msgid "English name" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:881 +#: lib/who_need_help_web/live/request_live/show.ex:992 #, elixir-autogen, elixir-format msgid "Enter handover code" msgstr "" @@ -752,51 +721,51 @@ msgstr "" msgid "Exact address after matching" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:270 +#: lib/who_need_help_web/live/activity_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Exact coordinates are shown only to participants you approve." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:165 +#: lib/who_need_help_web/live/profile_live.ex:168 #, elixir-autogen, elixir-format msgid "Exact for active match" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:314 -#: lib/who_need_help_web/live/request_live/show.ex:543 +#: lib/who_need_help_web/live/request_live/new.ex:316 +#: lib/who_need_help_web/live/request_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Exact only for active match" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:166 -#: lib/who_need_help_web/live/request_live/show.ex:544 +#: lib/who_need_help_web/live/profile_live.ex:169 +#: lib/who_need_help_web/live/request_live/show.ex:642 #, elixir-autogen, elixir-format msgid "Exact publicly" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/new.ex:317 #, elixir-autogen, elixir-format msgid "Exact publicly — explicit consent" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:602 +#: lib/who_need_help_web/live/request_live/show.ex:710 #, elixir-autogen, elixir-format msgid "Expires:" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:272 +#: lib/who_need_help_web/live/request_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Explain what is already arranged and what the volunteer needs to do." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:110 +#: lib/who_need_help_web/live/activity_live/index.ex:179 #, elixir-autogen, elixir-format msgid "Find people to join" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:645 -#: lib/who_need_help_web/live/moderation_live.ex:246 -#: lib/who_need_help_web/live/request_live/show.ex:975 +#: lib/who_need_help_web/live/activity_live/show.ex:664 +#: lib/who_need_help_web/live/moderation_live.ex:250 +#: lib/who_need_help_web/live/request_live/show.ex:1088 #, elixir-autogen, elixir-format msgid "Fraud" msgstr "" @@ -806,39 +775,39 @@ msgstr "" msgid "Free help and thanks" msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:51 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:62 #, elixir-autogen, elixir-format msgid "GitHub profile verified." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:250 +#: lib/who_need_help_web/live/profile_live.ex:253 #, elixir-autogen, elixir-format msgid "GitHub verification is unavailable until the operator configures its OAuth credentials. No manually entered link is treated as verified." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:864 +#: lib/who_need_help_web/live/request_live/show.ex:975 #, elixir-autogen, elixir-format msgid "HANDOVER CODE FOR THE HELPER" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:319 +#: lib/who_need_help_web/live/profile_live.ex:322 #, elixir-autogen, elixir-format msgid "Handle (optional)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:122 +#: lib/who_need_help_web/live/request_live/show.ex:147 #, elixir-autogen, elixir-format msgid "Handover code verified." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:643 -#: lib/who_need_help_web/live/moderation_live.ex:245 -#: lib/who_need_help_web/live/request_live/show.ex:974 +#: lib/who_need_help_web/live/activity_live/show.ex:662 +#: lib/who_need_help_web/live/moderation_live.ex:249 +#: lib/who_need_help_web/live/request_live/show.ex:1087 #, elixir-autogen, elixir-format msgid "Harassment" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:110 +#: lib/who_need_help_web/live/request_live/show.ex:135 #, elixir-autogen, elixir-format msgid "Help is in progress." msgstr "" @@ -853,12 +822,12 @@ msgstr "" msgid "Helper" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:767 +#: lib/who_need_help_web/live/request_live/show.ex:878 #, elixir-autogen, elixir-format msgid "Helper: %{name}" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:64 +#: lib/who_need_help_web/components/layouts.ex:176 #, elixir-autogen, elixir-format msgid "Helpers" msgstr "" @@ -883,26 +852,26 @@ msgstr "" msgid "Hi %{email},\n\nYou can log into your account by visiting the URL below:\n\n%{url}\n\nIf you didn't request this email, please ignore this." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:257 -#: lib/who_need_help_web/live/profile_live.ex:163 -#: lib/who_need_help_web/live/request_live/new.ex:313 -#: lib/who_need_help_web/live/request_live/show.ex:542 +#: lib/who_need_help_web/live/activity_live/new.ex:259 +#: lib/who_need_help_web/live/profile_live.ex:166 +#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/show.ex:640 #, elixir-autogen, elixir-format msgid "Hidden" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:346 +#: lib/who_need_help_web/live/moderation_live.ex:350 #, elixir-autogen, elixir-format msgid "Hide activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:332 -#: lib/who_need_help_web/live/moderation_live.ex:344 +#: lib/who_need_help_web/live/moderation_live.ex:336 +#: lib/who_need_help_web/live/moderation_live.ex:348 #, elixir-autogen, elixir-format msgid "Hide reason" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:334 +#: lib/who_need_help_web/live/moderation_live.ex:338 #, elixir-autogen, elixir-format msgid "Hide request" msgstr "" @@ -912,33 +881,33 @@ msgstr "" msgid "Human decisions retained" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48 #, elixir-autogen, elixir-format msgid "I am 18 or older and accept the safety rules" msgstr "" #: lib/who_need_help_web/controllers/page_html/home.html.heex:56 -#: lib/who_need_help_web/live/request_live/show.ex:751 +#: lib/who_need_help_web/live/request_live/show.ex:862 #, elixir-autogen, elixir-format msgid "I can help" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:124 +#: lib/who_need_help_web/live/request_live/new.ex:127 #, elixir-autogen, elixir-format msgid "I confirm there is no immediate danger, people and vehicles are away from active traffic or emergency services have already been contacted, and the requested task is lawful." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:118 +#: lib/who_need_help_web/live/request_live/new.ex:121 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, medical advice request, controlled substance, or request to buy medicine on my behalf." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:130 +#: lib/who_need_help_web/live/request_live/new.ex:133 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, there is no immediate danger, and the requested task is lawful." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:292 +#: lib/who_need_help_web/live/activity_live/new.ex:298 #, elixir-autogen, elixir-format msgid "I will approve participants individually, keep sensitive details in the approved group, and use an appropriate public first meeting point." msgstr "" @@ -948,93 +917,88 @@ msgstr "" msgid "Identity and reputation" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:66 +#: lib/who_need_help_web/controllers/user_session_controller.ex:89 #, elixir-autogen, elixir-format msgid "If your email is in our system, you will receive instructions for logging in shortly." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:647 -#: lib/who_need_help_web/live/moderation_live.ex:249 -#: lib/who_need_help_web/live/request_live/show.ex:978 +#: lib/who_need_help_web/live/activity_live/show.ex:666 +#: lib/who_need_help_web/live/moderation_live.ex:253 +#: lib/who_need_help_web/live/request_live/show.ex:1091 #, elixir-autogen, elixir-format msgid "Impersonation" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:916 +#: lib/who_need_help_web/live/request_live/show.ex:1027 #, elixir-autogen, elixir-format msgid "In a browser this works only while the page remains open. The Android app shows a persistent notification and can continue after it is minimized. Raw coordinates are removed when sharing stops." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:106 -#: lib/who_need_help_web/live/request_live/index.ex:107 -#: lib/who_need_help_web/live/request_live/show.ex:536 +#: lib/who_need_help_web/live/request_live/index.ex:180 +#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/show.ex:634 #, elixir-autogen, elixir-format msgid "In progress" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:151 +#: lib/who_need_help_web/live/profile_live.ex:154 #, elixir-autogen, elixir-format msgid "Interface language" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:617 +#: lib/who_need_help_web/live/moderation_live.ex:621 #, elixir-autogen, elixir-format msgid "Internal note" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:47 +#: lib/who_need_help_web/controllers/user_session_controller.ex:124 #, elixir-autogen, elixir-format msgid "Invalid email or password" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:301 +#: lib/who_need_help_web/live/activity_live/show.ex:314 #, elixir-autogen, elixir-format msgid "Join requests are closed." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:361 +#: lib/who_need_help_web/live/activity_live/show.ex:380 #, elixir-autogen, elixir-format msgid "Join requests close" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:237 +#: lib/who_need_help_web/live/activity_live/new.ex:239 #, elixir-autogen, elixir-format msgid "Join requests close (UTC)" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:54 -#, elixir-autogen, elixir-format -msgid "Keep me logged in on this device" -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:278 -#: lib/who_need_help_web/live/request_live/new.ex:336 +#: lib/who_need_help_web/live/activity_live/new.ex:282 +#: lib/who_need_help_web/live/request_live/new.ex:340 #, elixir-autogen, elixir-format msgid "Latitude" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:515 +#: lib/who_need_help_web/live/activity_live/show.ex:534 #, elixir-autogen, elixir-format msgid "Leave activity" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:914 +#: lib/who_need_help_web/live/request_live/show.ex:1025 #, elixir-autogen, elixir-format msgid "Live location" msgstr "" #: lib/who_need_help_web/controllers/page_html/safety.html.heex:51 -#: lib/who_need_help_web/live/activity_live/show.ex:484 +#: lib/who_need_help_web/live/activity_live/show.ex:503 #, elixir-autogen, elixir-format msgid "Location privacy" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:803 +#: lib/who_need_help_web/live/request_live/show.ex:914 #, elixir-autogen, elixir-format msgid "Location sharing is optional; missing evidence is not proof of abuse." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:310 +#: lib/who_need_help_web/live/request_live/new.ex:312 #, elixir-autogen, elixir-format msgid "Location visibility" msgstr "" @@ -1044,12 +1008,12 @@ msgstr "" msgid "Location-supported people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:609 +#: lib/who_need_help_web/live/request_live/show.ex:717 #, elixir-autogen, elixir-format msgid "Location:" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:69 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102 #, elixir-autogen, elixir-format msgid "Log in and stay logged in" msgstr "" @@ -1059,44 +1023,27 @@ msgstr "" msgid "Log in instructions" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:72 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105 #, elixir-autogen, elixir-format msgid "Log in only this time" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:45 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78 #, elixir-autogen, elixir-format msgid "Log in with email" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:60 -#, elixir-autogen, elixir-format -msgid "Log me in only this time" -msgstr "" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:102 +#: lib/who_need_help_web/controllers/user_session_controller.ex:117 #, elixir-autogen, elixir-format msgid "Logged out successfully." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:42 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:51 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:57 -#, elixir-autogen, elixir-format -msgid "Logging in..." -msgstr "" - -#: lib/who_need_help_web/live/activity_live/new.ex:285 -#: lib/who_need_help_web/live/request_live/new.ex:343 +#: lib/who_need_help_web/live/activity_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/new.ex:347 #, elixir-autogen, elixir-format msgid "Longitude" msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:95 -#, elixir-autogen, elixir-format -msgid "Magic link is invalid or it has expired." -msgstr "" - #: lib/who_need_help_web/controllers/feedback_html/show.html.heex:12 #, elixir-autogen, elixir-format msgid "Main Codex session ID" @@ -1107,12 +1054,12 @@ msgstr "" msgid "Manage your account email address and password settings" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:232 +#: lib/who_need_help_web/live/profile_live.ex:235 #, elixir-autogen, elixir-format msgid "Manually added links are public and marked unverified. A verified badge is only granted after completing a provider authorization flow." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:572 +#: lib/who_need_help_web/live/activity_live/show.ex:591 #, elixir-autogen, elixir-format msgid "Mark completed" msgstr "" @@ -1122,14 +1069,14 @@ msgstr "" msgid "Match and coordinate" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:104 -#: lib/who_need_help_web/live/request_live/index.ex:105 -#: lib/who_need_help_web/live/request_live/show.ex:534 +#: lib/who_need_help_web/live/request_live/index.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/show.ex:632 #, elixir-autogen, elixir-format msgid "Matched" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:110 +#: lib/who_need_help_web/live/request_live/new.ex:113 #, elixir-autogen, elixir-format msgid "Medicine is ready at the pharmacy" msgstr "" @@ -1144,7 +1091,7 @@ msgstr "" msgid "Medicine pickup" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:154 +#: lib/who_need_help_web/live/request_live/new.ex:156 #, elixir-autogen, elixir-format msgid "Medicine pickup remains the first priority. Roadside categories use the same moderated, extensible request flow with details specific to each type of help." msgstr "" @@ -1154,32 +1101,32 @@ msgstr "" msgid "Medicine requests" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:126 +#: lib/who_need_help_web/live/activity_live/index.ex:195 #, elixir-autogen, elixir-format msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:367 +#: lib/who_need_help_web/live/activity_live/show.ex:386 #, elixir-autogen, elixir-format msgid "Meeting area" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:548 +#: lib/who_need_help_web/live/moderation_live.ex:552 #, elixir-autogen, elixir-format msgid "Merge" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:542 +#: lib/who_need_help_web/live/moderation_live.ex:546 #, elixir-autogen, elixir-format msgid "Merge into" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:547 +#: lib/who_need_help_web/live/moderation_live.ex:551 #, elixir-autogen, elixir-format msgid "Merge note" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:464 +#: lib/who_need_help_web/live/activity_live/show.ex:483 #, elixir-autogen, elixir-format msgid "Message the approved group" msgstr "" @@ -1189,41 +1136,41 @@ msgstr "" msgid "Mode" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:73 +#: lib/who_need_help_web/components/layouts.ex:181 #: lib/who_need_help_web/live/moderation_live.ex:9 -#: lib/who_need_help_web/live/moderation_live.ex:287 +#: lib/who_need_help_web/live/moderation_live.ex:291 #, elixir-autogen, elixir-format msgid "Moderation" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:267 -#: lib/who_need_help_web/live/moderation_live.ex:633 +#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:637 #, elixir-autogen, elixir-format msgid "Moderator" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:226 -#: lib/who_need_help_web/user_auth.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/user_auth.ex:306 #, elixir-autogen, elixir-format msgid "Moderator access is required." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:122 +#: lib/who_need_help_web/live/request_live/index.ex:196 #, elixir-autogen, elixir-format msgid "NEARBY MUTUAL AID" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:64 +#: lib/who_need_help_web/live/request_live/index.ex:97 #, elixir-autogen, elixir-format msgid "Nearby help" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:113 +#: lib/who_need_help_web/live/request_live/new.ex:116 #, elixir-autogen, elixir-format msgid "Need roadside help in a safe location" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:301 +#: lib/who_need_help_web/live/profile_live.ex:304 #, elixir-autogen, elixir-format msgid "Network" msgstr "" @@ -1233,10 +1180,10 @@ msgstr "" msgid "New password" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:205 -#: lib/who_need_help_web/live/activity_live/show.ex:283 -#: lib/who_need_help_web/live/request_live/new.ex:253 -#: lib/who_need_help_web/live/request_live/show.ex:472 +#: lib/who_need_help_web/live/activity_live/new.ex:207 +#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/request_live/new.ex:255 +#: lib/who_need_help_web/live/request_live/show.ex:563 #, elixir-autogen, elixir-format msgid "No" msgstr "" @@ -1246,28 +1193,28 @@ msgstr "" msgid "No completed help yet." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:432 -#: lib/who_need_help_web/live/request_live/show.ex:663 +#: lib/who_need_help_web/live/activity_live/show.ex:451 +#: lib/who_need_help_web/live/request_live/show.ex:771 #, elixir-autogen, elixir-format msgid "No messages yet." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:157 +#: lib/who_need_help_web/live/activity_live/index.ex:227 #, elixir-autogen, elixir-format msgid "No open activities yet" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/index.ex:254 #, elixir-autogen, elixir-format msgid "No open requests yet" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:460 #, elixir-autogen, elixir-format msgid "No open signals." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:567 +#: lib/who_need_help_web/live/activity_live/show.ex:586 #, elixir-autogen, elixir-format msgid "No pending join requests." msgstr "" @@ -1277,17 +1224,17 @@ msgstr "" msgid "No proposals yet." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:372 +#: lib/who_need_help_web/live/moderation_live.ex:376 #, elixir-autogen, elixir-format msgid "No reports." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:258 +#: lib/who_need_help_web/live/profile_live.ex:261 #, elixir-autogen, elixir-format msgid "No social links added." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:209 +#: lib/who_need_help_web/live/profile_live.ex:212 #, elixir-autogen, elixir-format msgid "None revealed yet." msgstr "" @@ -1302,22 +1249,22 @@ msgstr "" msgid "Not for emergencies" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:94 -#: lib/who_need_help_web/live/request_live/index.ex:95 #: lib/who_need_help_web/live/request_live/index.ex:168 -#: lib/who_need_help_web/live/request_live/new.ex:291 -#: lib/who_need_help_web/live/request_live/show.ex:525 -#: lib/who_need_help_web/live/request_live/show.ex:526 +#: lib/who_need_help_web/live/request_live/index.ex:169 +#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/new.ex:293 +#: lib/who_need_help_web/live/request_live/show.ex:623 +#: lib/who_need_help_web/live/request_live/show.ex:624 #, elixir-autogen, elixir-format msgid "Now" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:108 +#: lib/who_need_help_web/live/activity_live/index.ex:177 #, elixir-autogen, elixir-format msgid "OPTIONAL SOCIAL PLANS" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:125 +#: lib/who_need_help_web/live/request_live/index.ex:199 #, elixir-autogen, elixir-format msgid "Only approximate areas are shown before a safe match." msgstr "" @@ -1327,27 +1274,27 @@ msgstr "" msgid "Only request pickup of a legal item already reserved or paid for. Do not request medical advice, a prescription, controlled substances, cash advances, repackaging, or a purchase on your behalf. Follow pharmacy rules and local law." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:390 +#: lib/who_need_help_web/live/moderation_live.ex:394 #, elixir-autogen, elixir-format msgid "Only the conversation linked to this report is displayed." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:418 +#: lib/who_need_help_web/live/activity_live/show.ex:437 #, elixir-autogen, elixir-format msgid "Only the organizer and approved participants can read or send these messages." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:97 -#: lib/who_need_help_web/live/activity_live/show.ex:319 -#: lib/who_need_help_web/live/moderation_live.ex:253 -#: lib/who_need_help_web/live/request_live/index.ex:102 -#: lib/who_need_help_web/live/request_live/index.ex:103 -#: lib/who_need_help_web/live/request_live/show.ex:533 +#: lib/who_need_help_web/live/activity_live/index.ex:166 +#: lib/who_need_help_web/live/activity_live/show.ex:338 +#: lib/who_need_help_web/live/moderation_live.ex:257 +#: lib/who_need_help_web/live/request_live/index.ex:176 +#: lib/who_need_help_web/live/request_live/index.ex:177 +#: lib/who_need_help_web/live/request_live/show.ex:631 #, elixir-autogen, elixir-format msgid "Open" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:420 +#: lib/who_need_help_web/live/moderation_live.ex:424 #, elixir-autogen, elixir-format msgid "Open abuse signals" msgstr "" @@ -1357,40 +1304,40 @@ msgstr "" msgid "Open proposals" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:171 +#: lib/who_need_help_web/live/profile_live.ex:174 #, elixir-autogen, elixir-format msgid "Optional external thank-you link" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:900 +#: lib/who_need_help_web/live/request_live/show.ex:1011 #, elixir-autogen, elixir-format msgid "Optional thank-you link" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:371 +#: lib/who_need_help_web/live/activity_live/show.ex:390 #, elixir-autogen, elixir-format msgid "Organizer" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:160 +#: lib/who_need_help_web/live/activity_live/show.ex:155 #, elixir-autogen, elixir-format msgid "Organizer blocked. Their activities and messages are hidden." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:520 +#: lib/who_need_help_web/live/activity_live/show.ex:539 #, elixir-autogen, elixir-format msgid "Organizer controls" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:380 +#: lib/who_need_help_web/live/activity_live/show.ex:399 #, elixir-autogen, elixir-format msgid "Organizer social links" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:648 -#: lib/who_need_help_web/live/moderation_live.ex:250 -#: lib/who_need_help_web/live/profile_live.ex:308 -#: lib/who_need_help_web/live/request_live/show.ex:979 +#: lib/who_need_help_web/live/activity_live/show.ex:667 +#: lib/who_need_help_web/live/moderation_live.ex:254 +#: lib/who_need_help_web/live/profile_live.ex:311 +#: lib/who_need_help_web/live/request_live/show.ex:1092 #, elixir-autogen, elixir-format msgid "Other" msgstr "" @@ -1400,57 +1347,57 @@ msgstr "" msgid "Parent (optional)" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:440 +#: lib/who_need_help_web/live/activity_live/show.ex:459 #, elixir-autogen, elixir-format msgid "Participant" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:64 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97 #, elixir-autogen, elixir-format msgid "Password" msgstr "" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:47 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 #, elixir-autogen, elixir-format msgid "Password updated successfully." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:278 +#: lib/who_need_help_web/live/request_live/new.ex:280 #, elixir-autogen, elixir-format msgid "Pickup instructions (optional)" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:578 +#: lib/who_need_help_web/live/request_live/show.ex:686 #, elixir-autogen, elixir-format msgid "Pickup notes" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:223 +#: lib/who_need_help_web/live/activity_live/new.ex:225 #, elixir-autogen, elixir-format msgid "Plan and expectations" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:402 +#: lib/who_need_help_web/live/activity_live/show.ex:421 #, elixir-autogen, elixir-format msgid "Plan details" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:520 +#: lib/who_need_help_web/live/request_live/show.ex:619 #, elixir-autogen, elixir-format msgid "Please check the entered data." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:235 +#: lib/who_need_help_web/live/moderation_live.ex:240 #, elixir-autogen, elixir-format msgid "Please check the submitted fields." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:314 +#: lib/who_need_help_web/live/activity_live/show.ex:334 #, elixir-autogen, elixir-format msgid "Please check the submitted message." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:305 +#: lib/who_need_help_web/live/request_live/new.ex:307 #, elixir-autogen, elixir-format msgid "Podil, near the central pharmacy" msgstr "" @@ -1460,12 +1407,13 @@ msgstr "" msgid "Post a clear request" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:45 +#: lib/who_need_help_web/components/layouts.ex:55 +#: lib/who_need_help_web/components/layouts.ex:87 #, elixir-autogen, elixir-format msgid "Primary navigation" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:647 +#: lib/who_need_help_web/live/request_live/show.ex:755 #, elixir-autogen, elixir-format msgid "Private match chat" msgstr "" @@ -1476,12 +1424,12 @@ msgid "Product boundaries, architecture decisions, Phoenix contexts, migrations, msgstr "" #: lib/who_need_help_web/live/profile_live.ex:37 -#, elixir-autogen, elixir-format, fuzzy +#, elixir-autogen, elixir-format msgid "Profile updated." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:976 +#: lib/who_need_help_web/live/moderation_live.ex:251 +#: lib/who_need_help_web/live/request_live/show.ex:1089 #, elixir-autogen, elixir-format msgid "Prohibited item" msgstr "" @@ -1516,27 +1464,27 @@ msgstr "" msgid "Public location can be hidden, approximate, or explicitly exact. Live tracking is optional. A browser shares only while its page is open; Android shows a persistent foreground-service notification while sharing. The current exact point is deleted when sharing stops, a match ends, or either participant blocks the other; derived movement and proximity evidence may remain." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:254 +#: lib/who_need_help_web/live/activity_live/new.ex:256 #, elixir-autogen, elixir-format msgid "Public map visibility" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:314 +#: lib/who_need_help_web/live/profile_live.ex:317 #, elixir-autogen, elixir-format msgid "Public profile URL" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:182 +#: lib/who_need_help_web/live/profile_live.ex:185 #, elixir-autogen, elixir-format msgid "Public reputation" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:115 +#: lib/who_need_help_web/live/activity_live/new.ex:117 #, elixir-autogen, elixir-format msgid "Publish a clear public plan. Join requests stay pending until you approve them." msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:301 +#: lib/who_need_help_web/live/activity_live/new.ex:310 #, elixir-autogen, elixir-format msgid "Publish activity" msgstr "" @@ -1546,13 +1494,13 @@ msgstr "" msgid "Publish proposal" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:355 +#: lib/who_need_help_web/live/request_live/new.ex:364 #, elixir-autogen, elixir-format msgid "Publish request" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:299 -#: lib/who_need_help_web/live/request_live/new.ex:353 +#: lib/who_need_help_web/live/activity_live/new.ex:308 +#: lib/who_need_help_web/live/request_live/new.ex:362 #, elixir-autogen, elixir-format msgid "Publishing…" msgstr "" @@ -1562,7 +1510,7 @@ msgstr "" msgid "READ BEFORE USING THE SERVICE" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:286 +#: lib/who_need_help_web/live/moderation_live.ex:290 #, elixir-autogen, elixir-format msgid "RESTRICTED WORKSPACE" msgstr "" @@ -1578,17 +1526,17 @@ msgid "Ranking prioritizes unique people helped with optional location-supported msgstr "" #: lib/who_need_help_web/live/leaderboard_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:712 +#: lib/who_need_help_web/live/request_live/show.ex:823 #, elixir-autogen, elixir-format msgid "Rating" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40 #, elixir-autogen, elixir-format msgid "Read the" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:641 +#: lib/who_need_help_web/live/activity_live/show.ex:660 #, elixir-autogen, elixir-format msgid "Reason" msgstr "" @@ -1598,38 +1546,38 @@ msgstr "" msgid "Register for an account" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:531 +#: lib/who_need_help_web/live/moderation_live.ex:535 #, elixir-autogen, elixir-format msgid "Reject" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:530 +#: lib/who_need_help_web/live/moderation_live.ex:534 #, elixir-autogen, elixir-format msgid "Rejection note" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:292 +#: lib/who_need_help_web/live/profile_live.ex:295 #, elixir-autogen, elixir-format msgid "Remove" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:449 +#: lib/who_need_help_web/live/activity_live/show.ex:468 #, elixir-autogen, elixir-format msgid "Report" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:681 +#: lib/who_need_help_web/live/request_live/show.ex:789 #, elixir-autogen, elixir-format msgid "Report message" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:971 +#: lib/who_need_help_web/live/request_live/show.ex:1084 #, elixir-autogen, elixir-format msgid "Report reason" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:128 -#: lib/who_need_help_web/live/request_live/show.ex:231 +#: lib/who_need_help_web/live/activity_live/show.ex:123 +#: lib/who_need_help_web/live/request_live/show.ex:261 #, elixir-autogen, elixir-format msgid "Report sent to moderators." msgstr "" @@ -1639,27 +1587,27 @@ msgstr "" msgid "Report updated." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:386 +#: lib/who_need_help_web/live/moderation_live.ex:390 #, elixir-autogen, elixir-format msgid "Reported evidence" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:293 +#: lib/who_need_help_web/live/moderation_live.ex:297 #, elixir-autogen, elixir-format msgid "Reports" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:940 +#: lib/who_need_help_web/live/request_live/show.ex:1053 #, elixir-autogen, elixir-format msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:102 +#: lib/who_need_help_web/live/request_live/show.ex:127 #, elixir-autogen, elixir-format msgid "Request cancelled." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:299 +#: lib/who_need_help_web/live/request_live/new.ex:301 #, elixir-autogen, elixir-format msgid "Request expires (UTC)" msgstr "" @@ -1670,86 +1618,86 @@ msgid "Request hidden." msgstr "" #: lib/who_need_help_web/live/moderation_live.ex:66 -#, elixir-autogen, elixir-format, fuzzy +#, elixir-autogen, elixir-format msgid "Request restored." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:502 +#: lib/who_need_help_web/live/activity_live/show.ex:521 #, elixir-autogen, elixir-format msgid "Request to join" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:617 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/show.ex:725 +#, elixir-autogen, elixir-format msgid "Requester links:" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:598 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/show.ex:706 +#, elixir-autogen, elixir-format msgid "Requester:" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:632 +#: lib/who_need_help_web/live/request_live/show.ex:740 #, elixir-autogen, elixir-format msgid "Requester: %{count} completed" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:367 +#: lib/who_need_help_web/live/moderation_live.ex:371 #, elixir-autogen, elixir-format msgid "Resolution note" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:255 -#: lib/who_need_help_web/live/moderation_live.ex:361 +#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:365 #, elixir-autogen, elixir-format msgid "Resolved" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:262 -#: lib/who_need_help_web/live/moderation_live.ex:611 +#: lib/who_need_help_web/live/moderation_live.ex:266 +#: lib/who_need_help_web/live/moderation_live.ex:615 #, elixir-autogen, elixir-format msgid "Restricted" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:451 +#: lib/who_need_help_web/live/moderation_live.ex:455 #, elixir-autogen, elixir-format msgid "Review note" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:203 +#: lib/who_need_help_web/live/request_live/show.ex:233 #, elixir-autogen, elixir-format msgid "Review saved. It appears after both participants review." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:257 -#: lib/who_need_help_web/live/moderation_live.ex:447 +#: lib/who_need_help_web/live/moderation_live.ex:261 +#: lib/who_need_help_web/live/moderation_live.ex:451 #, elixir-autogen, elixir-format msgid "Reviewed" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:254 -#: lib/who_need_help_web/live/moderation_live.ex:360 +#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:364 #, elixir-autogen, elixir-format msgid "Reviewing" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:571 +#: lib/who_need_help_web/live/moderation_live.ex:575 #, elixir-autogen, elixir-format msgid "Role" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:510 +#: lib/who_need_help_web/live/moderation_live.ex:514 #, elixir-autogen, elixir-format msgid "Russian description (optional)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:498 +#: lib/who_need_help_web/live/moderation_live.ex:502 #, elixir-autogen, elixir-format msgid "Russian name (optional)" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:616 -#: lib/who_need_help_web/live/request_live/show.ex:938 +#: lib/who_need_help_web/live/activity_live/show.ex:635 +#: lib/who_need_help_web/live/request_live/show.ex:1051 #, elixir-autogen, elixir-format msgid "Safety controls" msgstr "" @@ -1759,15 +1707,15 @@ msgstr "" msgid "Safety policy, category approval, account moderation, public launch, and every production infrastructure change remain human-controlled." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:104 +#: lib/who_need_help_web/components/layouts.ex:145 #: lib/who_need_help_web/controllers/page_html/safety.html.heex:6 #, elixir-autogen, elixir-format msgid "Safety rules" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:369 -#: lib/who_need_help_web/live/moderation_live.ex:452 -#: lib/who_need_help_web/live/moderation_live.ex:619 +#: lib/who_need_help_web/live/moderation_live.ex:373 +#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:623 #, elixir-autogen, elixir-format msgid "Save" msgstr "" @@ -1777,29 +1725,29 @@ msgstr "" msgid "Save Password" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:177 +#: lib/who_need_help_web/live/profile_live.ex:180 #, elixir-autogen, elixir-format msgid "Save profile" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:98 -#: lib/who_need_help_web/live/request_live/index.ex:99 -#: lib/who_need_help_web/live/request_live/index.ex:170 -#: lib/who_need_help_web/live/request_live/new.ex:293 -#: lib/who_need_help_web/live/request_live/show.ex:529 -#: lib/who_need_help_web/live/request_live/show.ex:530 +#: lib/who_need_help_web/live/request_live/index.ex:172 +#: lib/who_need_help_web/live/request_live/index.ex:173 +#: lib/who_need_help_web/live/request_live/index.ex:245 +#: lib/who_need_help_web/live/request_live/new.ex:295 +#: lib/who_need_help_web/live/request_live/show.ex:627 +#: lib/who_need_help_web/live/request_live/show.ex:628 #, elixir-autogen, elixir-format msgid "Scheduled" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:467 -#: lib/who_need_help_web/live/request_live/show.ex:696 +#: lib/who_need_help_web/live/activity_live/show.ex:486 +#: lib/who_need_help_web/live/request_live/show.ex:806 #, elixir-autogen, elixir-format msgid "Send" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:657 -#: lib/who_need_help_web/live/request_live/show.ex:988 +#: lib/who_need_help_web/live/activity_live/show.ex:676 +#: lib/who_need_help_web/live/request_live/show.ex:1101 #, elixir-autogen, elixir-format msgid "Send report" msgstr "" @@ -1809,28 +1757,23 @@ msgstr "" msgid "Set CODEX_SESSION_ID in the deployment environment before the submission recording." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:926 +#: lib/who_need_help_web/live/request_live/show.ex:1038 #, elixir-autogen, elixir-format msgid "Share location" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:280 +#: lib/who_need_help_web/live/request_live/new.ex:282 #, elixir-autogen, elixir-format msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:501 -#, elixir-autogen, elixir-format -msgid "Shared live location" -msgstr "" - -#: lib/who_need_help_web/live/profile_live.ex:147 +#: lib/who_need_help_web/live/profile_live.ex:150 #, elixir-autogen, elixir-format msgid "Short bio" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:217 -#: lib/who_need_help_web/live/request_live/new.ex:264 +#: lib/who_need_help_web/live/activity_live/new.ex:219 +#: lib/who_need_help_web/live/request_live/new.ex:266 #, elixir-autogen, elixir-format msgid "Short title" msgstr "" @@ -1845,12 +1788,12 @@ msgstr "" msgid "Signal updated." msgstr "" -#: lib/who_need_help_web/components/layouts/root.html.heex:52 +#: lib/who_need_help_web/components/layouts/root.html.heex:27 #, elixir-autogen, elixir-format msgid "Skip to main content" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:490 +#: lib/who_need_help_web/live/moderation_live.ex:494 #, elixir-autogen, elixir-format msgid "Slug" msgstr "" @@ -1871,12 +1814,12 @@ msgstr "" msgid "Social link added as unverified." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:67 +#: lib/who_need_help_web/live/profile_live.ex:74 #, elixir-autogen, elixir-format msgid "Social link was not found." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:230 +#: lib/who_need_help_web/live/profile_live.ex:233 #, elixir-autogen, elixir-format msgid "Social links" msgstr "" @@ -1886,54 +1829,54 @@ msgstr "" msgid "Social verification could not be started." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:77 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:88 #, elixir-autogen, elixir-format msgid "Social verification failed. Please retry." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:58 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 #, elixir-autogen, elixir-format msgid "Social verification session expired. Please retry." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:65 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:76 #, elixir-autogen, elixir-format msgid "Social verification session is invalid." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:646 -#: lib/who_need_help_web/live/moderation_live.ex:248 -#: lib/who_need_help_web/live/request_live/show.ex:977 +#: lib/who_need_help_web/live/activity_live/show.ex:665 +#: lib/who_need_help_web/live/moderation_live.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:1090 #, elixir-autogen, elixir-format msgid "Spam" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:829 +#: lib/who_need_help_web/live/request_live/show.ex:940 #, elixir-autogen, elixir-format msgid "Start helping" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:355 +#: lib/who_need_help_web/live/activity_live/show.ex:374 #, elixir-autogen, elixir-format msgid "Starts" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:232 +#: lib/who_need_help_web/live/activity_live/new.ex:234 #, elixir-autogen, elixir-format msgid "Starts at (UTC)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:572 +#: lib/who_need_help_web/live/moderation_live.ex:576 #, elixir-autogen, elixir-format msgid "Status" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:933 +#: lib/who_need_help_web/live/request_live/show.ex:1046 #, elixir-autogen, elixir-format msgid "Stop and delete position" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:515 +#: lib/who_need_help_web/live/moderation_live.ex:519 #, elixir-autogen, elixir-format msgid "Structured fields JSON" msgstr "" @@ -1943,34 +1886,34 @@ msgstr "" msgid "Structured fields must be a valid JSON object." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:726 +#: lib/who_need_help_web/live/request_live/show.ex:837 #, elixir-autogen, elixir-format msgid "Submit review" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:263 -#: lib/who_need_help_web/live/moderation_live.ex:612 +#: lib/who_need_help_web/live/moderation_live.ex:267 +#: lib/who_need_help_web/live/moderation_live.ex:616 #, elixir-autogen, elixir-format msgid "Suspended" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:509 +#: lib/who_need_help_web/live/request_live/show.ex:604 #, elixir-autogen, elixir-format msgid "That handover code is not correct." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:145 -#: lib/who_need_help_web/live/request_live/show.ex:248 +#: lib/who_need_help_web/live/activity_live/show.ex:140 +#: lib/who_need_help_web/live/request_live/show.ex:278 #, elixir-autogen, elixir-format msgid "That message cannot be reported from this view." msgstr "" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:80 #, elixir-autogen, elixir-format msgid "That social account is already linked." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:513 +#: lib/who_need_help_web/live/request_live/show.ex:608 #, elixir-autogen, elixir-format msgid "That step is not available in the current state." msgstr "" @@ -1985,27 +1928,27 @@ msgstr "" msgid "That verification provider is not supported." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:299 +#: lib/who_need_help_web/live/activity_live/show.ex:312 #, elixir-autogen, elixir-format msgid "The approved group has reached its capacity." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:180 +#: lib/who_need_help_web/live/request_live/show.ex:208 #, elixir-autogen, elixir-format msgid "The browser could not share your location." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:233 +#: lib/who_need_help_web/live/moderation_live.ex:237 #, elixir-autogen, elixir-format msgid "The last administrator cannot demote themselves." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:31 +#: lib/who_need_help_web/controllers/user_session_controller.ex:37 #, elixir-autogen, elixir-format msgid "The link is invalid or it has expired." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:507 +#: lib/who_need_help_web/live/activity_live/show.ex:526 #, elixir-autogen, elixir-format msgid "The organizer has not approved your request yet." msgstr "" @@ -2015,58 +1958,58 @@ msgstr "" msgid "The pharmacy closes soon and I cannot leave home. The item is already paid for." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:175 +#: lib/who_need_help_web/live/profile_live.ex:178 #, elixir-autogen, elixir-format msgid "The platform never handles, requires, splits, or guarantees a payment." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:489 +#: lib/who_need_help_web/live/activity_live/show.ex:508 #, elixir-autogen, elixir-format msgid "The public map is approximate or hidden. Exact coordinates appear only after approval." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:629 +#: lib/who_need_help_web/live/activity_live/show.ex:648 #, elixir-autogen, elixir-format msgid "The report is scoped to the selected group message." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:963 +#: lib/who_need_help_web/live/request_live/show.ex:1076 #, elixir-autogen, elixir-format msgid "The report will be scoped to the selected chat message." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:302 +#: lib/who_need_help_web/live/activity_live/show.ex:315 #, elixir-autogen, elixir-format msgid "This action is unavailable." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:309 +#: lib/who_need_help_web/live/activity_live/show.ex:322 #, elixir-autogen, elixir-format msgid "This activity is no longer open." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:307 +#: lib/who_need_help_web/live/activity_live/show.ex:320 #, elixir-autogen, elixir-format msgid "This activity state has already changed." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:518 +#: lib/who_need_help_web/live/request_live/show.ex:613 #, elixir-autogen, elixir-format msgid "This interaction is blocked." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:136 -#: lib/who_need_help_web/live/request_live/show.ex:557 +#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/request_live/show.ex:665 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:162 +#: lib/who_need_help_web/live/request_live/new.ex:164 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger. Never include prescription details, payment card data, access codes, or other sensitive information." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:227 +#: lib/who_need_help_web/live/moderation_live.ex:231 #, elixir-autogen, elixir-format msgid "This proposal has already been reviewed." msgstr "" @@ -2076,65 +2019,62 @@ msgstr "" msgid "This proposal is already closed." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:406 +#: lib/who_need_help_web/live/moderation_live.ex:410 #, elixir-autogen, elixir-format msgid "This report has no linked conversation." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:905 +#: lib/who_need_help_web/live/request_live/show.ex:1016 #, elixir-autogen, elixir-format msgid "This request already has a helper." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:52 +#: lib/who_need_help_web/live/request_live/show.ex:74 +#: lib/who_need_help_web/live/request_live/show.ex:615 #, elixir-autogen, elixir-format msgid "This request is no longer available." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:37 +#: lib/who_need_help_web/live/request_live/show.ex:59 #, elixir-autogen, elixir-format msgid "This request is unavailable." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:66 -#, elixir-autogen, elixir-format -msgid "Tip: If you prefer passwords, you can enable them in the user settings." -msgstr "" - #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:27 #, elixir-autogen, elixir-format msgid "To see sent emails, visit" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:96 -#: lib/who_need_help_web/live/request_live/index.ex:97 -#: lib/who_need_help_web/live/request_live/index.ex:169 -#: lib/who_need_help_web/live/request_live/new.ex:292 -#: lib/who_need_help_web/live/request_live/show.ex:527 -#: lib/who_need_help_web/live/request_live/show.ex:528 +#: lib/who_need_help_web/live/request_live/index.ex:170 +#: lib/who_need_help_web/live/request_live/index.ex:171 +#: lib/who_need_help_web/live/request_live/index.ex:244 +#: lib/who_need_help_web/live/request_live/new.ex:294 +#: lib/who_need_help_web/live/request_live/show.ex:625 +#: lib/who_need_help_web/live/request_live/show.ex:626 #, elixir-autogen, elixir-format msgid "Today" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:100 -#: lib/who_need_help_web/live/activity_live/show.ex:312 +#: lib/who_need_help_web/live/activity_live/new.ex:103 +#: lib/who_need_help_web/live/activity_live/show.ex:332 #: lib/who_need_help_web/live/category_proposal_live.ex:54 -#: lib/who_need_help_web/live/request_live/show.ex:519 +#: lib/who_need_help_web/live/profile_live.ex:59 +#: lib/who_need_help_web/live/request_live/show.ex:618 #, elixir-autogen, elixir-format msgid "Too many actions in the configured time window." msgstr "" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:45 +#: lib/who_need_help_web/controllers/user_registration_controller.ex:49 #, elixir-autogen, elixir-format msgid "Too many registration attempts in the configured time window." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:139 +#: lib/who_need_help_web/live/request_live/new.ex:142 #, elixir-autogen, elixir-format msgid "Too many requests in the configured time window." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:79 +#: lib/who_need_help_web/controllers/user_session_controller.ex:102 #, elixir-autogen, elixir-format msgid "Too many sign-in emails in the configured time window." msgstr "" @@ -2149,27 +2089,27 @@ msgstr "" msgid "Total" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:244 +#: lib/who_need_help_web/live/activity_live/new.ex:246 #, elixir-autogen, elixir-format msgid "Total group capacity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:506 +#: lib/who_need_help_web/live/moderation_live.ex:510 #, elixir-autogen, elixir-format msgid "Ukrainian description (optional)" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:494 +#: lib/who_need_help_web/live/moderation_live.ex:498 #, elixir-autogen, elixir-format msgid "Ukrainian name (optional)" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:342 +#: lib/who_need_help_web/live/profile_live.ex:345 #, elixir-autogen, elixir-format msgid "Unblock" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:956 +#: lib/who_need_help_web/live/request_live/show.ex:1069 #, elixir-autogen, elixir-format msgid "Unblock this user" msgstr "" @@ -2184,8 +2124,8 @@ msgstr "" msgid "Update email instructions" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:165 -#: lib/who_need_help_web/live/request_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/index.ex:240 +#: lib/who_need_help_web/live/request_live/new.ex:291 #, elixir-autogen, elixir-format msgid "Urgency" msgstr "" @@ -2196,23 +2136,23 @@ msgstr "" msgid "Urgent help" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:121 +#: lib/who_need_help_web/live/activity_live/new.ex:123 #, elixir-autogen, elixir-format msgid "Use a public first meeting point. Do not publish a home address, access code, travel document, or other sensitive information." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:194 +#: lib/who_need_help_web/components/layouts.ex:349 #, elixir-autogen, elixir-format msgid "Use dark theme" msgstr "" -#: lib/who_need_help_web/components/layouts.ex:185 +#: lib/who_need_help_web/components/layouts.ex:340 #, elixir-autogen, elixir-format msgid "Use light theme" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:267 -#: lib/who_need_help_web/live/request_live/new.ex:325 +#: lib/who_need_help_web/live/activity_live/new.ex:271 +#: lib/who_need_help_web/live/request_live/new.ex:329 #, elixir-autogen, elixir-format msgid "Use my current foreground location" msgstr "" @@ -2222,24 +2162,24 @@ msgstr "" msgid "Use private chat and optional live location sharing." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:176 +#: lib/who_need_help_web/components/layouts.ex:331 #, elixir-autogen, elixir-format msgid "Use system theme" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:266 -#: lib/who_need_help_web/live/moderation_live.ex:570 -#: lib/who_need_help_web/live/moderation_live.ex:632 +#: lib/who_need_help_web/live/moderation_live.ex:270 +#: lib/who_need_help_web/live/moderation_live.ex:574 +#: lib/who_need_help_web/live/moderation_live.ex:636 #, elixir-autogen, elixir-format msgid "User" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:271 +#: lib/who_need_help_web/live/request_live/show.ex:307 #, elixir-autogen, elixir-format msgid "User blocked. Their requests and new messages are hidden." msgstr "" -#: lib/who_need_help_web/controllers/user_session_controller.ex:19 +#: lib/who_need_help_web/controllers/user_session_controller.ex:25 #, elixir-autogen, elixir-format msgid "User confirmed successfully." msgstr "" @@ -2254,8 +2194,8 @@ msgstr "" msgid "User status updated." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:80 -#: lib/who_need_help_web/live/request_live/show.ex:285 +#: lib/who_need_help_web/live/profile_live.ex:87 +#: lib/who_need_help_web/live/request_live/show.ex:321 #, elixir-autogen, elixir-format msgid "User unblocked." msgstr "" @@ -2265,12 +2205,12 @@ msgstr "" msgid "Verified people" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:242 +#: lib/who_need_help_web/live/profile_live.ex:245 #, elixir-autogen, elixir-format msgid "Verify GitHub" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:885 +#: lib/who_need_help_web/live/request_live/show.ex:996 #, elixir-autogen, elixir-format msgid "Verify handover" msgstr "" @@ -2280,28 +2220,23 @@ msgstr "" msgid "Verify the handover" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:323 +#: lib/who_need_help_web/live/moderation_live.ex:327 #, elixir-autogen, elixir-format msgid "View scoped evidence" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:207 +#: lib/who_need_help_web/live/profile_live.ex:210 #, elixir-autogen, elixir-format msgid "Visible reviews" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:755 +#: lib/who_need_help_web/live/request_live/show.ex:866 #, elixir-autogen, elixir-format msgid "Waiting for a nearby volunteer." msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:4 -#, elixir-autogen, elixir-format -msgid "Welcome %{email}" -msgstr "" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:20 -#: lib/who_need_help_web/controllers/user_session_controller.ex:40 +#: lib/who_need_help_web/controllers/user_session_controller.ex:26 +#: lib/who_need_help_web/controllers/user_session_controller.ex:51 #, elixir-autogen, elixir-format msgid "Welcome back!" msgstr "" @@ -2311,8 +2246,8 @@ msgstr "" msgid "What Codex contributed" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:654 -#: lib/who_need_help_web/live/request_live/show.ex:985 +#: lib/who_need_help_web/live/activity_live/show.ex:673 +#: lib/who_need_help_web/live/request_live/show.ex:1098 #, elixir-autogen, elixir-format msgid "What happened?" msgstr "" @@ -2322,7 +2257,7 @@ msgstr "" msgid "What help category is missing?" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:270 +#: lib/who_need_help_web/live/request_live/new.ex:272 #, elixir-autogen, elixir-format msgid "What help do you need?" msgstr "" @@ -2337,12 +2272,12 @@ msgstr "" msgid "Who Need Help coordinates voluntary help between adults. It cannot verify every person, request, item, route, or outcome and cannot guarantee safety." msgstr "" -#: lib/who_need_help_web/components/layouts.ex:40 +#: lib/who_need_help_web/components/layouts.ex:43 #, elixir-autogen, elixir-format msgid "Who Need Help home" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:54 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53 #, elixir-autogen, elixir-format msgid "Who Need Help is not an emergency or medical service. Medicine requests are limited to pickup of legal items already purchased or reserved." msgstr "" @@ -2352,7 +2287,7 @@ msgstr "" msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:123 +#: lib/who_need_help_web/live/request_live/index.ex:197 #, elixir-autogen, elixir-format msgid "Who needs help?" msgstr "" @@ -2362,46 +2297,46 @@ msgstr "" msgid "Why is this useful?" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:846 +#: lib/who_need_help_web/live/request_live/show.ex:957 #, elixir-autogen, elixir-format msgid "Withdraw from this request" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:693 +#: lib/who_need_help_web/live/request_live/show.ex:802 #, elixir-autogen, elixir-format msgid "Write a safe coordination message…" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:202 -#: lib/who_need_help_web/live/activity_live/show.ex:281 -#: lib/who_need_help_web/live/request_live/new.ex:250 -#: lib/who_need_help_web/live/request_live/show.ex:470 +#: lib/who_need_help_web/live/activity_live/new.ex:204 +#: lib/who_need_help_web/live/activity_live/show.ex:294 +#: lib/who_need_help_web/live/request_live/new.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:561 #, elixir-autogen, elixir-format msgid "Yes" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/activity_live/show.ex:309 #, elixir-autogen, elixir-format msgid "You already have a join request for this activity." msgstr "" -#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#: lib/who_need_help_web/live/category_proposal_live.ex:58 #, elixir-autogen, elixir-format msgid "You already voted for this proposal." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:487 +#: lib/who_need_help_web/live/activity_live/show.ex:506 #, elixir-autogen, elixir-format msgid "You are approved, so the map can show the exact meeting coordinates." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:92 +#: lib/who_need_help_web/live/request_live/show.ex:117 #, elixir-autogen, elixir-format msgid "You are matched. Use chat to coordinate safely." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:304 -#: lib/who_need_help_web/live/request_live/show.ex:510 +#: lib/who_need_help_web/live/activity_live/show.ex:317 +#: lib/who_need_help_web/live/request_live/show.ex:605 #, elixir-autogen, elixir-format msgid "You are not allowed to do that." msgstr "" @@ -2411,44 +2346,44 @@ msgstr "" msgid "You are running the local mail adapter." msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:328 +#: lib/who_need_help_web/live/request_live/new.ex:332 #, elixir-autogen, elixir-format msgid "You can also enter coordinates manually for local testing." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/index.ex:256 #, elixir-autogen, elixir-format msgid "You can be the first person to ask the community." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:507 +#: lib/who_need_help_web/live/request_live/show.ex:602 #, elixir-autogen, elixir-format msgid "You cannot accept your own request." msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:303 +#: lib/who_need_help_web/live/activity_live/show.ex:316 #, elixir-autogen, elixir-format msgid "You cannot report your own content." msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/live/moderation_live.ex:234 #, elixir-autogen, elixir-format msgid "You cannot restrict your own moderator account." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:330 +#: lib/who_need_help_web/live/profile_live.ex:333 #, elixir-autogen, elixir-format msgid "You have not blocked anyone." msgstr "" -#: lib/who_need_help_web/user_auth.ex:208 -#: lib/who_need_help_web/user_auth.ex:240 -#: lib/who_need_help_web/user_auth.ex:270 +#: lib/who_need_help_web/user_auth.ex:256 +#: lib/who_need_help_web/user_auth.ex:288 +#: lib/who_need_help_web/user_auth.ex:318 #, elixir-autogen, elixir-format msgid "You must log in to access this page." msgstr "" -#: lib/who_need_help_web/user_auth.ex:178 +#: lib/who_need_help_web/user_auth.ex:226 #, elixir-autogen, elixir-format msgid "You must re-authenticate to access this page." msgstr "" @@ -2458,22 +2393,22 @@ msgstr "" msgid "You need to reauthenticate to perform sensitive actions on your account." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:117 +#: lib/who_need_help_web/live/request_live/show.ex:142 #, elixir-autogen, elixir-format msgid "You withdrew from this request." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:221 +#: lib/who_need_help_web/live/activity_live/index.ex:292 #, elixir-autogen, elixir-format msgid "Your activities" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:113 +#: lib/who_need_help_web/live/request_live/show.ex:138 #, elixir-autogen, elixir-format msgid "Your confirmation was saved." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:139 +#: lib/who_need_help_web/live/profile_live.ex:142 #, elixir-autogen, elixir-format msgid "Your profile" msgstr "" @@ -2483,52 +2418,52 @@ msgstr "" msgid "Your request is now visible to nearby helpers." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:266 +#: lib/who_need_help_web/live/request_live/index.ex:342 #, elixir-autogen, elixir-format msgid "Your requests" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:706 +#: lib/who_need_help_web/live/request_live/show.ex:817 #, elixir-autogen, elixir-format msgid "Your review is revealed only after both participants submit." msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/index.ex:319 #, elixir-autogen, elixir-format msgid "Your trust summary" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:307 +#: lib/who_need_help_web/live/moderation_live.ex:311 #, elixir-autogen, elixir-format msgid "activity" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:310 +#: lib/who_need_help_web/live/moderation_live.ex:314 #, elixir-autogen, elixir-format msgid "activity message" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 #, elixir-autogen, elixir-format msgid "before confirming." msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:190 +#: lib/who_need_help_web/live/activity_live/index.ex:261 #: lib/who_need_help_web/live/category_proposal_live.ex:147 -#: lib/who_need_help_web/live/moderation_live.ex:313 -#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/moderation_live.ex:317 +#: lib/who_need_help_web/live/request_live/index.ex:286 #, elixir-autogen, elixir-format msgid "by %{name}" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:186 -#: lib/who_need_help_web/live/request_live/index.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:772 +#: lib/who_need_help_web/live/profile_live.ex:189 +#: lib/who_need_help_web/live/request_live/index.ex:323 +#: lib/who_need_help_web/live/request_live/show.ex:883 #, elixir-autogen, elixir-format msgid "completed" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:215 +#: lib/who_need_help_web/live/request_live/index.ex:291 #, elixir-autogen, elixir-format msgid "expires %{time}" msgstr "" @@ -2538,59 +2473,59 @@ msgstr "" msgid "for an account now." msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:799 +#: lib/who_need_help_web/live/request_live/show.ex:910 #, elixir-autogen, elixir-format msgid "helper movement supported" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:800 +#: lib/who_need_help_web/live/request_live/show.ex:911 #, elixir-autogen, elixir-format msgid "no movement evidence" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:791 +#: lib/who_need_help_web/live/request_live/show.ex:902 #, elixir-autogen, elixir-format msgid "no proximity evidence" msgstr "" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82 #, elixir-autogen, elixir-format msgid "or" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:586 +#: lib/who_need_help_web/live/activity_live/show.ex:605 #, elixir-autogen, elixir-format msgid "organizer" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:648 +#: lib/who_need_help_web/live/request_live/show.ex:756 #, elixir-autogen, elixir-format msgid "participants only" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:252 -#: lib/who_need_help_web/live/request_live/show.ex:776 +#: lib/who_need_help_web/live/request_live/index.ex:328 +#: lib/who_need_help_web/live/request_live/show.ex:887 #, elixir-autogen, elixir-format msgid "people" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:790 +#: lib/who_need_help_web/live/request_live/show.ex:901 #, elixir-autogen, elixir-format msgid "proximity supported" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:201 -#: lib/who_need_help_web/live/request_live/show.ex:780 +#: lib/who_need_help_web/live/profile_live.ex:204 +#: lib/who_need_help_web/live/request_live/show.ex:891 #, elixir-autogen, elixir-format msgid "rating" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:640 +#: lib/who_need_help_web/live/request_live/show.ex:748 #, elixir-autogen, elixir-format msgid "rating %{rating}" msgstr "" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 #, elixir-autogen, elixir-format msgid "safety rules" msgstr "" @@ -2605,17 +2540,17 @@ msgstr "" msgid "to your account now." msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:191 +#: lib/who_need_help_web/live/profile_live.ex:194 #, elixir-autogen, elixir-format msgid "unique people" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:395 -#: lib/who_need_help_web/live/activity_live/show.ex:546 -#: lib/who_need_help_web/live/activity_live/show.ex:605 -#: lib/who_need_help_web/live/profile_live.ex:274 -#: lib/who_need_help_web/live/request_live/show.ex:627 -#: lib/who_need_help_web/live/request_live/show.ex:821 +#: lib/who_need_help_web/live/activity_live/show.ex:414 +#: lib/who_need_help_web/live/activity_live/show.ex:565 +#: lib/who_need_help_web/live/activity_live/show.ex:624 +#: lib/who_need_help_web/live/profile_live.ex:277 +#: lib/who_need_help_web/live/request_live/show.ex:735 +#: lib/who_need_help_web/live/request_live/show.ex:932 #, elixir-autogen, elixir-format msgid "unverified" msgstr "" @@ -2625,120 +2560,258 @@ msgstr "" msgid "urgent" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:394 -#: lib/who_need_help_web/live/activity_live/show.ex:545 -#: lib/who_need_help_web/live/activity_live/show.ex:604 -#: lib/who_need_help_web/live/profile_live.ex:273 -#: lib/who_need_help_web/live/request_live/index.ex:257 -#: lib/who_need_help_web/live/request_live/show.ex:626 -#: lib/who_need_help_web/live/request_live/show.ex:820 +#: lib/who_need_help_web/live/activity_live/show.ex:413 +#: lib/who_need_help_web/live/activity_live/show.ex:564 +#: lib/who_need_help_web/live/activity_live/show.ex:623 +#: lib/who_need_help_web/live/profile_live.ex:276 +#: lib/who_need_help_web/live/request_live/index.ex:333 +#: lib/who_need_help_web/live/request_live/show.ex:734 +#: lib/who_need_help_web/live/request_live/show.ex:931 #, elixir-autogen, elixir-format msgid "verified" msgstr "" -#: lib/who_need_help_web/live/profile_live.ex:196 +#: lib/who_need_help_web/live/profile_live.ex:199 #, elixir-autogen, elixir-format msgid "verified handovers" msgstr "" -#: lib/who_need_help_web/live/request_live/show.ex:552 +#: lib/who_need_help_web/live/request_live/show.ex:660 #, elixir-autogen, elixir-format msgid "← All requests" msgstr "" -#: lib/who_need_help_web/live/activity_live/new.ex:111 -#: lib/who_need_help_web/live/activity_live/show.ex:329 +#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/show.ex:348 #, elixir-autogen, elixir-format msgid "← Back to activities" msgstr "" -#: lib/who_need_help_web/live/request_live/new.ex:150 +#: lib/who_need_help_web/live/request_live/new.ex:152 #, elixir-autogen, elixir-format msgid "← Back to requests" msgstr "" #: lib/who_need_help_web/live/leaderboard_live.ex:37 -#, elixir-autogen, elixir-format, fuzzy +#, elixir-autogen, elixir-format msgid "← Requests" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:258 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/moderation_live.ex:262 +#, elixir-autogen, elixir-format msgid "Approved" msgstr "" -#: lib/who_need_help_web/live/request_live/index.ex:112 -#: lib/who_need_help_web/live/request_live/index.ex:113 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/request_live/index.ex:186 +#: lib/who_need_help_web/live/request_live/index.ex:187 +#, elixir-autogen, elixir-format msgid "Expired" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:275 -#, elixir-autogen, elixir-format -msgid "Handover without location evidence" -msgstr "" - -#: lib/who_need_help_web/live/moderation_live.ex:277 -#, elixir-autogen, elixir-format -msgid "Location without movement" -msgstr "" - #: lib/who_need_help_web/live/moderation_live.ex:279 #, elixir-autogen, elixir-format +msgid "Handover without location evidence" +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:281 +#, elixir-autogen, elixir-format +msgid "Location without movement" +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:283 +#, elixir-autogen, elixir-format msgid "Manual review" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:260 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/moderation_live.ex:264 +#, elixir-autogen, elixir-format msgid "Merged" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:278 +#: lib/who_need_help_web/live/moderation_live.ex:282 #, elixir-autogen, elixir-format msgid "Reciprocal rating ring" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:259 -#, elixir-autogen, elixir-format, fuzzy +#: lib/who_need_help_web/live/moderation_live.ex:263 +#, elixir-autogen, elixir-format msgid "Rejected" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:276 #, elixir-autogen, elixir-format msgid "Repeated participant pair" msgstr "" -#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:275 #, elixir-autogen, elixir-format msgid "Unusual action rate" msgstr "" -#: lib/who_need_help_web/live/activity_live/show.ex:429 -#: lib/who_need_help_web/live/request_live/show.ex:660 +#: lib/who_need_help_web/live/activity_live/show.ex:448 +#: lib/who_need_help_web/live/request_live/show.ex:768 #, elixir-autogen, elixir-format msgid "Load earlier messages" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:202 -#: lib/who_need_help_web/live/activity_live/index.ex:238 +#: lib/who_need_help_web/live/activity_live/index.ex:273 +#: lib/who_need_help_web/live/activity_live/index.ex:310 #: lib/who_need_help_web/live/category_proposal_live.ex:162 #: lib/who_need_help_web/live/leaderboard_live.ex:87 -#: lib/who_need_help_web/live/moderation_live.ex:380 -#: lib/who_need_help_web/live/moderation_live.ex:415 -#: lib/who_need_help_web/live/moderation_live.ex:560 -#: lib/who_need_help_web/live/moderation_live.ex:652 -#: lib/who_need_help_web/live/profile_live.ex:221 -#: lib/who_need_help_web/live/profile_live.ex:351 -#: lib/who_need_help_web/live/request_live/index.ex:227 -#: lib/who_need_help_web/live/request_live/index.ex:283 +#: lib/who_need_help_web/live/moderation_live.ex:384 +#: lib/who_need_help_web/live/moderation_live.ex:419 +#: lib/who_need_help_web/live/moderation_live.ex:564 +#: lib/who_need_help_web/live/moderation_live.ex:656 +#: lib/who_need_help_web/live/profile_live.ex:224 +#: lib/who_need_help_web/live/profile_live.ex:354 +#: lib/who_need_help_web/live/request_live/index.ex:303 +#: lib/who_need_help_web/live/request_live/index.ex:360 #, elixir-autogen, elixir-format msgid "Load more" msgstr "" -#: lib/who_need_help_web/live/activity_live/index.ex:213 -#: lib/who_need_help_web/live/activity_live/show.ex:479 -#: lib/who_need_help_web/live/request_live/index.ex:238 -#: lib/who_need_help_web/live/request_live/show.ex:738 +#: lib/who_need_help_web/live/activity_live/index.ex:284 +#: lib/who_need_help_web/live/activity_live/show.ex:498 +#: lib/who_need_help_web/live/request_live/index.ex:314 +#: lib/who_need_help_web/live/request_live/show.ex:849 #, elixir-autogen, elixir-format msgid "The map is unavailable in this browser. Request details remain usable." msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:4 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Confirm email change" +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:5 +#, elixir-autogen, elixir-format +msgid "Confirm the new email address for your account." +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:242 +#, elixir-autogen, elixir-format +msgid "Could not complete moderation. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:336 +#: lib/who_need_help_web/live/category_proposal_live.ex:60 +#: lib/who_need_help_web/live/request_live/show.ex:621 +#, elixir-autogen, elixir-format +msgid "Could not complete the action. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/new.ex:105 +#, elixir-autogen, elixir-format +msgid "Could not publish the activity. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/request_live/new.ex:144 +#, elixir-autogen, elixir-format +msgid "Could not publish the request. Please try again." +msgstr "" + +#: lib/who_need_help_web/live/profile_live.ex:91 +#, elixir-autogen, elixir-format +msgid "Could not unblock this user. Please try again." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:22 +#, elixir-autogen, elixir-format +msgid "JavaScript is required to confirm this protected email link." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:594 +#, elixir-autogen, elixir-format +msgid "Last update: %{time}" +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:617 +#, elixir-autogen, elixir-format +msgid "Live location sharing is not active." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:592 +#, elixir-autogen, elixir-format +msgid "Shared location" +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:329 +#, elixir-autogen, elixir-format +msgid "The organizer cannot leave the activity." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:326 +#, elixir-autogen, elixir-format +msgid "The organizer is already attending." +msgstr "" + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "The registration form is invalid." +msgstr "" + +#: lib/who_need_help_web/live/moderation_live.ex:239 +#, elixir-autogen, elixir-format +msgid "The selected record is no longer available." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "The settings form is invalid." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_controller.ex:111 +#, elixir-autogen, elixir-format +msgid "The sign-in form is invalid." +msgstr "" + +#: lib/who_need_help_web/live/activity_live/show.ex:323 +#, elixir-autogen, elixir-format +msgid "This activity is no longer available." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:616 +#, elixir-autogen, elixir-format +msgid "This match is no longer active." +msgstr "" + +#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#, elixir-autogen, elixir-format +msgid "This proposal is no longer available." +msgstr "" + +#: lib/who_need_help_web/live/request_live/show.ex:614 +#, elixir-autogen, elixir-format +msgid "This request has expired." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "Too many sign-in attempts in the configured time window." +msgstr "" + +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:46 +#, elixir-autogen, elixir-format +msgid "Your secure email link is ready. Continue to sign in." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:128 +#, elixir-autogen, elixir-format +msgid "The confirmation email could not be sent. Please try again." +msgstr "" + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:109 +#, elixir-autogen, elixir-format +msgid "If this address can be registered or signed in, login instructions will arrive shortly." +msgstr "If this address can be registered or signed in, login instructions will arrive shortly." + +#: lib/who_need_help_web/live/profile_live.ex:62 +#, elixir-autogen, elixir-format +msgid "This account cannot perform that action." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:40 +#, elixir-autogen, elixir-format +msgid "Too many email-change requests in the configured time window." +msgstr "Too many email-change requests in the configured time window." diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po index d7563b7..42078e1 100644 --- a/priv/gettext/en/LC_MESSAGES/errors.po +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -116,12 +116,12 @@ msgstr "" msgid "contains an invalid field definition" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:22 +#: lib/who_need_help_web/validation_messages.ex:23 #, elixir-autogen, elixir-format msgid "did not change" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:23 +#: lib/who_need_help_web/validation_messages.ex:24 #, elixir-autogen, elixir-format msgid "does not match password" msgstr "" @@ -131,16 +131,11 @@ msgstr "" msgid "field keys must be unique" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:19 +#: lib/who_need_help_web/validation_messages.ex:20 #, elixir-autogen, elixir-format msgid "must be a GitHub profile URL" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:18 -#, elixir-autogen, elixir-format -msgid "must be a complete http(s) URL" -msgstr "" - #: lib/who_need_help_web/validation_messages.ex:12 #, elixir-autogen, elixir-format msgid "must be an object with a fields array" @@ -166,7 +161,7 @@ msgstr "" msgid "must contain non-empty localized descriptions" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:21 +#: lib/who_need_help_web/validation_messages.ex:22 #, elixir-autogen, elixir-format msgid "must have the @ sign and no spaces" msgstr "" @@ -186,7 +181,17 @@ msgstr "" msgid "select exactly one report target" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:20 +#: lib/who_need_help_web/validation_messages.ex:21 #, elixir-autogen, elixir-format msgid "you must confirm that you are 18+ and accept the rules" msgstr "" + +#: lib/who_need_help_web/validation_messages.ex:18 +#, elixir-autogen, elixir-format +msgid "must be a complete HTTPS URL" +msgstr "must be a complete HTTPS URL" + +#: lib/who_need_help_web/validation_messages.ex:19 +#, elixir-autogen, elixir-format +msgid "must be a full HTTPS URL" +msgstr "must be a full HTTPS URL" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index 60352b3..f16e602 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -113,12 +113,12 @@ msgstr "" msgid "contains an invalid field definition" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:22 +#: lib/who_need_help_web/validation_messages.ex:23 #, elixir-autogen, elixir-format msgid "did not change" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:23 +#: lib/who_need_help_web/validation_messages.ex:24 #, elixir-autogen, elixir-format msgid "does not match password" msgstr "" @@ -128,16 +128,11 @@ msgstr "" msgid "field keys must be unique" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:19 +#: lib/who_need_help_web/validation_messages.ex:20 #, elixir-autogen, elixir-format msgid "must be a GitHub profile URL" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:18 -#, elixir-autogen, elixir-format -msgid "must be a complete http(s) URL" -msgstr "" - #: lib/who_need_help_web/validation_messages.ex:12 #, elixir-autogen, elixir-format msgid "must be an object with a fields array" @@ -163,7 +158,7 @@ msgstr "" msgid "must contain non-empty localized descriptions" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:21 +#: lib/who_need_help_web/validation_messages.ex:22 #, elixir-autogen, elixir-format msgid "must have the @ sign and no spaces" msgstr "" @@ -183,7 +178,17 @@ msgstr "" msgid "select exactly one report target" msgstr "" -#: lib/who_need_help_web/validation_messages.ex:20 +#: lib/who_need_help_web/validation_messages.ex:21 #, elixir-autogen, elixir-format msgid "you must confirm that you are 18+ and accept the rules" msgstr "" + +#: lib/who_need_help_web/validation_messages.ex:18 +#, elixir-autogen, elixir-format +msgid "must be a complete HTTPS URL" +msgstr "" + +#: lib/who_need_help_web/validation_messages.ex:19 +#, elixir-autogen, elixir-format +msgid "must be a full HTTPS URL" +msgstr "" diff --git a/priv/gettext/ru/LC_MESSAGES/default.po b/priv/gettext/ru/LC_MESSAGES/default.po index 3ae97d2..749fc6b 100644 --- a/priv/gettext/ru/LC_MESSAGES/default.po +++ b/priv/gettext/ru/LC_MESSAGES/default.po @@ -11,30 +11,32 @@ msgstr "" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100 != 11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10||n%100>=20) ? 1 : 2);\n" -#: lib/who_need_help_web/components/layouts/root.html.heex:67 +#: lib/who_need_help_web/components/layouts.ex:111 +#: lib/who_need_help_web/components/layouts.ex:257 #, elixir-autogen, elixir-format msgid "Account settings" msgstr "Настройки аккаунта" -#: lib/who_need_help_web/components/core_components.ex:375 +#: lib/who_need_help_web/components/core_components.ex:393 #, elixir-autogen, elixir-format msgid "Actions" msgstr "Действия" -#: lib/who_need_help_web/components/layouts.ex:83 +#: lib/who_need_help_web/components/layouts.ex:70 +#: lib/who_need_help_web/components/layouts.ex:119 #: lib/who_need_help_web/controllers/page_html/home.html.heex:21 #: lib/who_need_help_web/live/request_live/new.ex:22 #, elixir-autogen, elixir-format msgid "Ask for help" msgstr "Попросить о помощи" -#: lib/who_need_help_web/components/layouts.ex:140 -#: lib/who_need_help_web/components/layouts.ex:155 +#: lib/who_need_help_web/components/layouts.ex:295 +#: lib/who_need_help_web/components/layouts.ex:310 #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "Пытаемся восстановить соединение" -#: lib/who_need_help_web/components/layouts.ex:59 +#: lib/who_need_help_web/components/layouts.ex:171 #, elixir-autogen, elixir-format msgid "Categories" msgstr "Категории" @@ -54,33 +56,34 @@ msgstr "Помощь может быть ближе, чем кажется." msgid "Join the community" msgstr "Присоединиться к сообществу" -#: lib/who_need_help_web/components/layouts.ex:89 -#: lib/who_need_help_web/components/layouts/root.html.heex:77 +#: lib/who_need_help_web/components/layouts.ex:78 +#: lib/who_need_help_web/components/layouts.ex:128 #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:9 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:45 #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:5 #, elixir-autogen, elixir-format msgid "Log in" msgstr "Войти" -#: lib/who_need_help_web/components/layouts/root.html.heex:70 +#: lib/who_need_help_web/components/layouts.ex:114 +#: lib/who_need_help_web/components/layouts.ex:260 #, elixir-autogen, elixir-format msgid "Log out" msgstr "Выйти" -#: lib/who_need_help_web/components/layouts.ex:78 -#: lib/who_need_help_web/components/layouts/root.html.heex:64 +#: lib/who_need_help_web/components/layouts.ex:108 +#: lib/who_need_help_web/components/layouts.ex:254 #: lib/who_need_help_web/live/profile_live.ex:16 #, elixir-autogen, elixir-format msgid "Profile" msgstr "Профиль" -#: lib/who_need_help_web/components/layouts/root.html.heex:74 +#: lib/who_need_help_web/components/layouts.ex:75 +#: lib/who_need_help_web/components/layouts.ex:125 #, elixir-autogen, elixir-format msgid "Register" msgstr "Регистрация" -#: lib/who_need_help_web/components/layouts.ex:49 +#: lib/who_need_help_web/components/layouts.ex:161 #, elixir-autogen, elixir-format msgid "Requests" msgstr "Заявки" @@ -90,12 +93,12 @@ msgstr "Заявки" msgid "See nearby requests" msgstr "Заявки рядом" -#: lib/who_need_help_web/components/layouts.ex:147 +#: lib/who_need_help_web/components/layouts.ex:302 #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "Что-то пошло не так!" -#: lib/who_need_help_web/components/layouts.ex:132 +#: lib/who_need_help_web/components/layouts.ex:287 #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "Нет соединения с интернетом" @@ -105,67 +108,67 @@ msgstr "Нет соединения с интернетом" msgid "close" msgstr "закрыть" -#: lib/who_need_help_web/live/activity_live/show.ex:344 +#: lib/who_need_help_web/live/activity_live/show.ex:363 #, elixir-autogen, elixir-format msgid "%{approved}/%{capacity} approved" msgstr "Одобрено: %{approved}/%{capacity}" -#: lib/who_need_help_web/live/moderation_live.ex:481 +#: lib/who_need_help_web/live/moderation_live.ex:485 #, elixir-autogen, elixir-format msgid "%{count} community votes" msgstr "Голосов сообщества: %{count}" -#: lib/who_need_help_web/live/request_live/show.ex:637 +#: lib/who_need_help_web/live/request_live/show.ex:745 #, elixir-autogen, elixir-format msgid "%{count} unique people" msgstr "%{count} уникальных людей" -#: lib/who_need_help_web/live/request_live/show.ex:718 +#: lib/who_need_help_web/live/request_live/show.ex:829 #, elixir-autogen, elixir-format msgid "1 — unsafe" msgstr "1 — небезопасно" -#: lib/who_need_help_web/live/request_live/show.ex:717 +#: lib/who_need_help_web/live/request_live/show.ex:828 #, elixir-autogen, elixir-format msgid "2 — poor" msgstr "2 — плохо" -#: lib/who_need_help_web/live/request_live/show.ex:716 +#: lib/who_need_help_web/live/request_live/show.ex:827 #, elixir-autogen, elixir-format msgid "3 — okay" msgstr "3 — нормально" -#: lib/who_need_help_web/live/request_live/show.ex:715 +#: lib/who_need_help_web/live/request_live/show.ex:826 #, elixir-autogen, elixir-format msgid "4 — good" msgstr "4 — хорошо" -#: lib/who_need_help_web/live/request_live/show.ex:714 +#: lib/who_need_help_web/live/request_live/show.ex:825 #, elixir-autogen, elixir-format msgid "5 — excellent" msgstr "5 — отлично" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:31 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:118 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" "Ссылка для подтверждения изменения электронной почты была отправлена на " "новый адрес." -#: lib/who_need_help_web/live/moderation_live.ex:433 +#: lib/who_need_help_web/live/moderation_live.ex:437 #, elixir-autogen, elixir-format msgid "A signal requests human review; optional tracking absence is not proof of abuse." msgstr "" "Сигнал требует проверки человеком; отсутствие необязательного отслеживания " "не является доказательством злоупотребления." -#: lib/who_need_help_web/live/request_live/show.ex:748 +#: lib/who_need_help_web/live/request_live/show.ex:859 #, elixir-autogen, elixir-format msgid "Accept only if you can complete this safely and for free." msgstr "" "Принимайте заявку, только если можете безопасно и бесплатно её выполнить." -#: lib/who_need_help_web/live/request_live/show.ex:535 +#: lib/who_need_help_web/live/request_live/show.ex:633 #, elixir-autogen, elixir-format msgid "Accepted" msgstr "Принято" @@ -175,34 +178,30 @@ msgstr "Принято" msgid "Account Settings" msgstr "Настройки аккаунта" -#: lib/who_need_help_web/components/layouts/root.html.heex:54 +#: lib/who_need_help_web/components/layouts.ex:63 +#: lib/who_need_help_web/components/layouts.ex:192 #, elixir-autogen, elixir-format msgid "Account and language" msgstr "Аккаунт и язык" -#: lib/who_need_help_web/live/moderation_live.ex:565 +#: lib/who_need_help_web/live/moderation_live.ex:569 #, elixir-autogen, elixir-format msgid "Accounts" msgstr "Аккаунты" -#: lib/who_need_help_web/live/request_live/show.ex:744 +#: lib/who_need_help_web/live/request_live/show.ex:855 #, elixir-autogen, elixir-format msgid "Action center" msgstr "Центр действий" -#: lib/who_need_help_web/live/moderation_live.ex:261 -#: lib/who_need_help_web/live/moderation_live.ex:610 +#: lib/who_need_help_web/live/moderation_live.ex:265 +#: lib/who_need_help_web/live/moderation_live.ex:614 #, elixir-autogen, elixir-format msgid "Active" msgstr "Активный" -#: lib/who_need_help_web/live/request_live/show.ex:502 -#, elixir-autogen, elixir-format -msgid "Active match only" -msgstr "Только для активной заявки" - -#: lib/who_need_help_web/components/layouts.ex:54 -#: lib/who_need_help_web/live/activity_live/index.ex:68 +#: lib/who_need_help_web/components/layouts.ex:166 +#: lib/who_need_help_web/live/activity_live/index.ex:118 #, elixir-autogen, elixir-format msgid "Activities" msgstr "Активности" @@ -218,7 +217,7 @@ msgstr "" "походов согласуйте маршрут и меры безопасности и никогда не публикуйте " "домашние адреса, коды доступа или документы для поездки." -#: lib/who_need_help_web/live/activity_live/new.ex:161 +#: lib/who_need_help_web/live/activity_live/new.ex:163 #, elixir-autogen, elixir-format msgid "Activity details" msgstr "Детали активности" @@ -228,8 +227,8 @@ msgstr "Детали активности" msgid "Activity hidden." msgstr "Активность скрыта." -#: lib/who_need_help_web/live/activity_live/show.ex:16 -#: lib/who_need_help_web/live/activity_live/show.ex:182 +#: lib/who_need_help_web/live/activity_live/show.ex:18 +#: lib/who_need_help_web/live/activity_live/show.ex:177 #, elixir-autogen, elixir-format msgid "Activity is unavailable." msgstr "Активность недоступна." @@ -244,29 +243,29 @@ msgstr "Активность опубликована. Вы одобряете msgid "Activity restored." msgstr "Активность восстановлена." -#: lib/who_need_help_web/live/activity_live/index.ex:140 -#: lib/who_need_help_web/live/activity_live/new.ex:137 +#: lib/who_need_help_web/live/activity_live/index.ex:210 +#: lib/who_need_help_web/live/activity_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Activity type" msgstr "Тип активности" -#: lib/who_need_help_web/live/profile_live.ex:322 +#: lib/who_need_help_web/live/profile_live.ex:325 #, elixir-autogen, elixir-format msgid "Add unverified link" msgstr "Добавить неподтверждённую ссылку" -#: lib/who_need_help_web/live/moderation_live.ex:268 -#: lib/who_need_help_web/live/moderation_live.ex:634 +#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:638 #, elixir-autogen, elixir-format msgid "Administrator" msgstr "Администратор" -#: lib/who_need_help_web/live/activity_live/index.ex:142 +#: lib/who_need_help_web/live/activity_live/index.ex:212 #, elixir-autogen, elixir-format msgid "All activities" msgstr "Все активности" -#: lib/who_need_help_web/live/request_live/index.ex:152 +#: lib/who_need_help_web/live/request_live/index.ex:227 #, elixir-autogen, elixir-format msgid "All categories" msgstr "Все категории" @@ -276,72 +275,65 @@ msgstr "Все категории" msgid "Already registered?" msgstr "Уже зарегистрированы?" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:29 -#, elixir-autogen, elixir-format -msgid "An email was sent to %{email}, please access it to confirm your account." -msgstr "" -"Мы отправили письмо на %{email}. Откройте его, чтобы подтвердить учётную " -"запись." - -#: lib/who_need_help_web/live/request_live/show.ex:508 +#: lib/who_need_help_web/live/request_live/show.ex:603 #, elixir-autogen, elixir-format msgid "Another helper already accepted this request." msgstr "Другой помощник уже принял эту заявку." -#: lib/who_need_help_web/live/request_live/index.ex:167 +#: lib/who_need_help_web/live/request_live/index.ex:242 #, elixir-autogen, elixir-format msgid "Any urgency" msgstr "Любая срочность" -#: lib/who_need_help_web/live/activity_live/show.ex:505 +#: lib/who_need_help_web/live/activity_live/show.ex:524 #, elixir-autogen, elixir-format msgid "Approval pending" msgstr "Ожидает одобрения" -#: lib/who_need_help_web/live/activity_live/show.ex:555 +#: lib/who_need_help_web/live/activity_live/show.ex:574 #, elixir-autogen, elixir-format msgid "Approve" msgstr "Одобрить" -#: lib/who_need_help_web/live/activity_live/show.ex:416 +#: lib/who_need_help_web/live/activity_live/show.ex:435 #, elixir-autogen, elixir-format msgid "Approved group chat" msgstr "Чат одобренной группы" -#: lib/who_need_help_web/live/activity_live/show.ex:581 +#: lib/who_need_help_web/live/activity_live/show.ex:600 #, elixir-autogen, elixir-format msgid "Approved participants" msgstr "Одобренные участники" #: lib/who_need_help_web/controllers/page_html/home.html.heex:51 -#: lib/who_need_help_web/live/activity_live/new.ex:256 -#: lib/who_need_help_web/live/request_live/new.ex:304 +#: lib/who_need_help_web/live/activity_live/new.ex:258 +#: lib/who_need_help_web/live/request_live/new.ex:306 #, elixir-autogen, elixir-format msgid "Approximate area" msgstr "Примерный район" -#: lib/who_need_help_web/live/activity_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Approximate public meeting area" msgstr "Примерный публичный район встречи" -#: lib/who_need_help_web/live/profile_live.ex:164 -#: lib/who_need_help_web/live/request_live/show.ex:541 +#: lib/who_need_help_web/live/profile_live.ex:167 +#: lib/who_need_help_web/live/request_live/show.ex:639 #, elixir-autogen, elixir-format msgid "Approximate publicly" msgstr "Публично — приблизительно" -#: lib/who_need_help_web/live/request_live/new.ex:312 +#: lib/who_need_help_web/live/request_live/new.ex:314 #, elixir-autogen, elixir-format msgid "Approximate publicly (recommended)" msgstr "Показывать приблизительно (рекомендуется)" -#: lib/who_need_help_web/live/request_live/show.ex:594 +#: lib/who_need_help_web/live/request_live/show.ex:702 #, elixir-autogen, elixir-format msgid "Area:" msgstr "Район:" -#: lib/who_need_help_web/live/request_live/new.ex:152 +#: lib/who_need_help_web/live/request_live/new.ex:154 #, elixir-autogen, elixir-format msgid "Ask for urgent help" msgstr "Попросить срочную помощь" @@ -366,17 +358,17 @@ msgstr "" msgid "Block and report" msgstr "Блокировка и жалоба" -#: lib/who_need_help_web/live/activity_live/show.ex:622 +#: lib/who_need_help_web/live/activity_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Block organizer" msgstr "Заблокировать организатора" -#: lib/who_need_help_web/live/request_live/show.ex:949 +#: lib/who_need_help_web/live/request_live/show.ex:1062 #, elixir-autogen, elixir-format msgid "Block this user" msgstr "Заблокировать пользователя" -#: lib/who_need_help_web/live/profile_live.ex:328 +#: lib/who_need_help_web/live/profile_live.ex:331 #, elixir-autogen, elixir-format msgid "Blocked users" msgstr "Блокированные пользователи" @@ -387,12 +379,12 @@ msgid "Both confirm and the helper enters a one-time code." msgstr "" "Оба участника подтверждают завершение, а помощник вводит одноразовый код." -#: lib/who_need_help_web/live/request_live/new.ex:115 +#: lib/who_need_help_web/live/request_live/new.ex:118 #, elixir-autogen, elixir-format msgid "Briefly describe the help you need" msgstr "Кратко опишите, какая помощь вам нужна" -#: lib/who_need_help_web/components/layouts.ex:106 +#: lib/who_need_help_web/components/layouts.ex:147 #, elixir-autogen, elixir-format msgid "Build Week feedback" msgstr "Отчёт для Build Week" @@ -412,28 +404,28 @@ msgstr "ДОВЕРИЕ СООБЩЕСТВА" msgid "COMMUNITY-DRIVEN TAXONOMY" msgstr "КАТЕГОРИИ ОТ СООБЩЕСТВА" -#: lib/who_need_help_web/live/activity_live/show.ex:575 +#: lib/who_need_help_web/live/activity_live/show.ex:594 #, elixir-autogen, elixir-format msgid "Cancel activity" msgstr "Отменить активность" -#: lib/who_need_help_web/live/request_live/show.ex:762 -#: lib/who_need_help_web/live/request_live/show.ex:856 +#: lib/who_need_help_web/live/request_live/show.ex:873 +#: lib/who_need_help_web/live/request_live/show.ex:967 #, elixir-autogen, elixir-format msgid "Cancel request" msgstr "Отменить заявку" -#: lib/who_need_help_web/live/activity_live/index.ex:99 -#: lib/who_need_help_web/live/activity_live/show.ex:321 -#: lib/who_need_help_web/live/request_live/index.ex:110 -#: lib/who_need_help_web/live/request_live/index.ex:111 -#: lib/who_need_help_web/live/request_live/show.ex:538 +#: lib/who_need_help_web/live/activity_live/index.ex:168 +#: lib/who_need_help_web/live/activity_live/show.ex:340 +#: lib/who_need_help_web/live/request_live/index.ex:184 +#: lib/who_need_help_web/live/request_live/index.ex:185 +#: lib/who_need_help_web/live/request_live/show.ex:636 #, elixir-autogen, elixir-format msgid "Cancelled" msgstr "Отменено" -#: lib/who_need_help_web/live/request_live/index.ex:150 -#: lib/who_need_help_web/live/request_live/new.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:225 +#: lib/who_need_help_web/live/request_live/new.ex:180 #, elixir-autogen, elixir-format msgid "Category" msgstr "Категория" @@ -443,14 +435,14 @@ msgstr "Категория" msgid "Category created and proposal approved." msgstr "Категория создана и предложение одобрено." -#: lib/who_need_help_web/live/request_live/new.ex:209 -#: lib/who_need_help_web/live/request_live/show.ex:583 +#: lib/who_need_help_web/live/request_live/new.ex:211 +#: lib/who_need_help_web/live/request_live/show.ex:691 #, elixir-autogen, elixir-format msgid "Category details" msgstr "Детали категории" #: lib/who_need_help_web/live/category_proposal_live.ex:66 -#: lib/who_need_help_web/live/moderation_live.ex:462 +#: lib/who_need_help_web/live/moderation_live.ex:466 #, elixir-autogen, elixir-format msgid "Category proposals" msgstr "Предложения категорий" @@ -460,7 +452,7 @@ msgstr "Предложения категорий" msgid "Change Email" msgstr "Изменить электронную почту" -#: lib/who_need_help_web/live/moderation_live.ex:638 +#: lib/who_need_help_web/live/moderation_live.ex:642 #, elixir-autogen, elixir-format msgid "Change role" msgstr "Изменить роль" @@ -477,53 +469,53 @@ msgid "Choose a category, urgency, and safe location visibility." msgstr "" "Выберите категорию, срочность и безопасный уровень видимости геопозиции." -#: lib/who_need_help_web/live/activity_live/new.ex:138 +#: lib/who_need_help_web/live/activity_live/new.ex:140 #, elixir-autogen, elixir-format msgid "Choose activity" msgstr "Выберите активность" -#: lib/who_need_help_web/live/request_live/new.ex:179 +#: lib/who_need_help_web/live/request_live/new.ex:181 #, elixir-autogen, elixir-format msgid "Choose category" msgstr "Выберите категорию" -#: lib/who_need_help_web/live/activity_live/new.ex:173 -#: lib/who_need_help_web/live/activity_live/new.ex:200 -#: lib/who_need_help_web/live/request_live/new.ex:221 -#: lib/who_need_help_web/live/request_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:175 +#: lib/who_need_help_web/live/activity_live/new.ex:202 +#: lib/who_need_help_web/live/request_live/new.ex:223 +#: lib/who_need_help_web/live/request_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Choose…" msgstr "Выберите…" -#: lib/who_need_help_web/live/activity_live/new.ex:249 +#: lib/who_need_help_web/live/activity_live/new.ex:251 #, elixir-autogen, elixir-format msgid "City centre, near the main entrance" msgstr "Центр города, рядом с главным входом" -#: lib/who_need_help_web/live/activity_live/show.ex:635 -#: lib/who_need_help_web/live/request_live/show.ex:965 +#: lib/who_need_help_web/live/activity_live/show.ex:654 +#: lib/who_need_help_web/live/request_live/show.ex:1078 #, elixir-autogen, elixir-format msgid "Clear" msgstr "Очистить" -#: lib/who_need_help_web/live/moderation_live.ex:387 +#: lib/who_need_help_web/live/moderation_live.ex:391 #, elixir-autogen, elixir-format msgid "Close" msgstr "Закрыть" -#: lib/who_need_help_web/live/activity_live/new.ex:218 +#: lib/who_need_help_web/live/activity_live/new.ex:220 #, elixir-autogen, elixir-format msgid "Coffee and conversation" msgstr "Кофе и разговоры" -#: lib/who_need_help_web/live/activity_live/index.ex:113 +#: lib/who_need_help_web/live/activity_live/index.ex:182 #, elixir-autogen, elixir-format msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgstr "" "Кофе, кино, прогулки и походы отделены от срочной помощи и никогда не влияют" " на рейтинг помощника." -#: lib/who_need_help_web/live/request_live/show.ex:724 +#: lib/who_need_help_web/live/request_live/show.ex:835 #, elixir-autogen, elixir-format msgid "Comment (optional)" msgstr "Комментарий (необязательно)" @@ -533,35 +525,35 @@ msgstr "Комментарий (необязательно)" msgid "Community helpers" msgstr "Помощники сообщества" -#: lib/who_need_help_web/live/activity_live/index.ex:191 -#: lib/who_need_help_web/live/activity_live/show.ex:373 -#: lib/who_need_help_web/live/activity_live/show.ex:527 -#: lib/who_need_help_web/live/activity_live/show.ex:584 -#: lib/who_need_help_web/live/request_live/index.ex:211 +#: lib/who_need_help_web/live/activity_live/index.ex:262 +#: lib/who_need_help_web/live/activity_live/show.ex:392 +#: lib/who_need_help_web/live/activity_live/show.ex:546 +#: lib/who_need_help_web/live/activity_live/show.ex:603 +#: lib/who_need_help_web/live/request_live/index.ex:287 #, elixir-autogen, elixir-format msgid "Community member" msgstr "Член сообщества" -#: lib/who_need_help_web/live/activity_live/index.ex:98 -#: lib/who_need_help_web/live/activity_live/show.ex:320 -#: lib/who_need_help_web/live/request_live/index.ex:108 -#: lib/who_need_help_web/live/request_live/index.ex:109 -#: lib/who_need_help_web/live/request_live/show.ex:537 +#: lib/who_need_help_web/live/activity_live/index.ex:167 +#: lib/who_need_help_web/live/activity_live/show.ex:339 +#: lib/who_need_help_web/live/request_live/index.ex:182 +#: lib/who_need_help_web/live/request_live/index.ex:183 +#: lib/who_need_help_web/live/request_live/show.ex:635 #, elixir-autogen, elixir-format msgid "Completed" msgstr "Завершено" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:27 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:61 #, elixir-autogen, elixir-format msgid "Confirm and log in only this time" msgstr "Подтвердить и войти только сейчас" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:21 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:55 #, elixir-autogen, elixir-format msgid "Confirm and stay logged in" msgstr "Подтвердить и оставаться в системе" -#: lib/who_need_help_web/live/request_live/show.ex:836 +#: lib/who_need_help_web/live/request_live/show.ex:947 #, elixir-autogen, elixir-format msgid "Confirm my part is complete" msgstr "Подтвердить выполнение своей части" @@ -572,13 +564,13 @@ msgid "Confirm new password" msgstr "Подтвердите новый пароль" #: lib/who_need_help_web/live/category_proposal_live.ex:51 -#: lib/who_need_help_web/live/request_live/show.ex:516 +#: lib/who_need_help_web/live/request_live/show.ex:611 #, elixir-autogen, elixir-format msgid "Confirm your account and ensure it is active first." msgstr "Подтвердите свою учетную запись и убедитесь, что она активна." -#: lib/who_need_help_web/live/activity_live/new.ex:97 -#: lib/who_need_help_web/live/request_live/new.ex:136 +#: lib/who_need_help_web/live/activity_live/new.ex:100 +#: lib/who_need_help_web/live/request_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Confirm your email and ensure your account is active before publishing." msgstr "" @@ -590,129 +582,103 @@ msgstr "" msgid "Confirmation instructions" msgstr "Инструкции по подтверждению" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:18 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:24 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:52 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:58 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:15 #, elixir-autogen, elixir-format msgid "Confirming..." msgstr "Подтверждение…" -#: lib/who_need_help_web/live/moderation_live.ex:238 -#, elixir-autogen, elixir-format -msgid "Could not complete moderation: %{reason}" -msgstr "Не удалось завершить модерацию: %{reason}" - -#: lib/who_need_help_web/live/activity_live/show.ex:317 -#: lib/who_need_help_web/live/category_proposal_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:523 -#, elixir-autogen, elixir-format -msgid "Could not complete the action: %{reason}" -msgstr "Не удалось выполнить действие: %{reason}" - -#: lib/who_need_help_web/live/activity_live/new.ex:103 -#, elixir-autogen, elixir-format -msgid "Could not publish the activity: %{reason}" -msgstr "Не удалось опубликовать активность: %{reason}" - -#: lib/who_need_help_web/live/request_live/new.ex:142 -#, elixir-autogen, elixir-format -msgid "Could not publish the request: %{reason}" -msgstr "Не удалось опубликовать заявку: %{reason}" - -#: lib/who_need_help_web/live/profile_live.ex:87 -#, elixir-autogen, elixir-format -msgid "Could not unblock: %{reason}" -msgstr "Не удалось разблокировать: %{reason}" - -#: lib/who_need_help_web/live/activity_live/index.ex:159 +#: lib/who_need_help_web/live/activity_live/index.ex:229 #, elixir-autogen, elixir-format msgid "Create a clear plan in a public place." msgstr "Создайте четкий план в общественном месте." -#: lib/who_need_help_web/live/activity_live/index.ex:119 +#: lib/who_need_help_web/live/activity_live/index.ex:188 #: lib/who_need_help_web/live/activity_live/new.ex:11 #, elixir-autogen, elixir-format msgid "Create activity" msgstr "Создать активность" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:60 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 #, elixir-autogen, elixir-format msgid "Create an account" msgstr "Создать аккаунт" -#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/new.ex:115 #, elixir-autogen, elixir-format msgid "Create an activity" msgstr "Создать активность" -#: lib/who_need_help_web/live/moderation_live.ex:521 +#: lib/who_need_help_web/live/moderation_live.ex:525 #, elixir-autogen, elixir-format msgid "Create category" msgstr "Создать категорию" -#: lib/who_need_help_web/live/request_live/index.ex:129 +#: lib/who_need_help_web/live/request_live/index.ex:203 #, elixir-autogen, elixir-format msgid "Create urgent request" msgstr "Создать срочную заявку" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:58 #, elixir-autogen, elixir-format msgid "Creating account..." msgstr "Создание аккаунта..." -#: lib/who_need_help_web/live/activity_live/show.ex:644 +#: lib/who_need_help_web/live/activity_live/show.ex:663 #, elixir-autogen, elixir-format msgid "Dangerous plan" msgstr "Опасный план" -#: lib/who_need_help_web/live/moderation_live.ex:244 -#: lib/who_need_help_web/live/request_live/show.ex:973 +#: lib/who_need_help_web/live/moderation_live.ex:248 +#: lib/who_need_help_web/live/request_live/show.ex:1086 #, elixir-autogen, elixir-format msgid "Dangerous request" msgstr "Опасный запрос" -#: lib/who_need_help_web/live/moderation_live.ex:573 +#: lib/who_need_help_web/live/moderation_live.ex:577 #, elixir-autogen, elixir-format msgid "Decision" msgstr "Решение" -#: lib/who_need_help_web/live/moderation_live.ex:519 +#: lib/who_need_help_web/live/moderation_live.ex:523 #, elixir-autogen, elixir-format msgid "Decision note" msgstr "Примечание к решению" -#: lib/who_need_help_web/live/moderation_live.ex:289 +#: lib/who_need_help_web/live/moderation_live.ex:293 #, elixir-autogen, elixir-format msgid "Decisions and access to reported chat evidence are written to the audit log." msgstr "" "Решения и доступ к материалам жалоб на переписку записываются в журнал " "аудита." -#: lib/who_need_help_web/live/activity_live/show.ex:562 +#: lib/who_need_help_web/live/activity_live/show.ex:581 #, elixir-autogen, elixir-format msgid "Decline" msgstr "Отклонить" -#: lib/who_need_help_web/live/profile_live.ex:161 +#: lib/who_need_help_web/live/profile_live.ex:164 #, elixir-autogen, elixir-format msgid "Default location visibility" msgstr "Видимость местоположения по умолчанию" -#: lib/who_need_help_web/live/activity_live/new.ex:225 +#: lib/who_need_help_web/live/activity_live/new.ex:227 #, elixir-autogen, elixir-format msgid "Describe the public plan, approximate duration, and who might enjoy it." msgstr "" "Опишите публичный план, примерную продолжительность и кому он может быть " "интересен." -#: lib/who_need_help_web/live/moderation_live.ex:256 -#: lib/who_need_help_web/live/moderation_live.ex:362 -#: lib/who_need_help_web/live/moderation_live.ex:448 +#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:366 +#: lib/who_need_help_web/live/moderation_live.ex:452 #, elixir-autogen, elixir-format msgid "Dismissed" msgstr "Отклонено" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:35 -#: lib/who_need_help_web/live/profile_live.ex:146 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34 +#: lib/who_need_help_web/live/profile_live.ex:149 #, elixir-autogen, elixir-format msgid "Display name" msgstr "Отображаемое имя" @@ -730,25 +696,26 @@ msgstr "" msgid "Don't have an account?" msgstr "Нет аккаунта?" -#: lib/who_need_help_web/live/request_live/show.ex:704 +#: lib/who_need_help_web/live/request_live/show.ex:815 #, elixir-autogen, elixir-format msgid "Double-blind review" msgstr "Двусторонний скрытый отзыв" #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:25 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:38 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:56 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:71 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89 #: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15 #, elixir-autogen, elixir-format msgid "Email" msgstr "Электронная почта" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:65 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:89 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:96 #, elixir-autogen, elixir-format msgid "Email change link is invalid or it has expired." msgstr "Ссылка на изменение электронной почты недействительна или истекла." -#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:84 #, elixir-autogen, elixir-format msgid "Email changed successfully." msgstr "Адрес электронной почты успешно изменён." @@ -763,22 +730,22 @@ msgstr "" "передачи и открытые отзывы и встречайтесь только там, где чувствуете себя " "безопасно." -#: lib/who_need_help_web/live/profile_live.ex:153 +#: lib/who_need_help_web/live/profile_live.ex:156 #, elixir-autogen, elixir-format msgid "English" msgstr "Английский" -#: lib/who_need_help_web/live/moderation_live.ex:502 +#: lib/who_need_help_web/live/moderation_live.ex:506 #, elixir-autogen, elixir-format msgid "English description" msgstr "Описание на английском" -#: lib/who_need_help_web/live/moderation_live.ex:491 +#: lib/who_need_help_web/live/moderation_live.ex:495 #, elixir-autogen, elixir-format msgid "English name" msgstr "Название на английском" -#: lib/who_need_help_web/live/request_live/show.ex:881 +#: lib/who_need_help_web/live/request_live/show.ex:992 #, elixir-autogen, elixir-format msgid "Enter handover code" msgstr "Введите код передачи" @@ -788,51 +755,51 @@ msgstr "Введите код передачи" msgid "Exact address after matching" msgstr "Точный адрес после назначения помощника" -#: lib/who_need_help_web/live/activity_live/new.ex:270 +#: lib/who_need_help_web/live/activity_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Exact coordinates are shown only to participants you approve." msgstr "Точные координаты показаны только участникам, которых вы одобряете." -#: lib/who_need_help_web/live/profile_live.ex:165 +#: lib/who_need_help_web/live/profile_live.ex:168 #, elixir-autogen, elixir-format msgid "Exact for active match" msgstr "Точно — для активной заявки" -#: lib/who_need_help_web/live/request_live/new.ex:314 -#: lib/who_need_help_web/live/request_live/show.ex:543 +#: lib/who_need_help_web/live/request_live/new.ex:316 +#: lib/who_need_help_web/live/request_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Exact only for active match" msgstr "Точно только для активной заявки" -#: lib/who_need_help_web/live/profile_live.ex:166 -#: lib/who_need_help_web/live/request_live/show.ex:544 +#: lib/who_need_help_web/live/profile_live.ex:169 +#: lib/who_need_help_web/live/request_live/show.ex:642 #, elixir-autogen, elixir-format msgid "Exact publicly" msgstr "Публично — точно" -#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/new.ex:317 #, elixir-autogen, elixir-format msgid "Exact publicly — explicit consent" msgstr "Публично — точно, с явным согласием" -#: lib/who_need_help_web/live/request_live/show.ex:602 +#: lib/who_need_help_web/live/request_live/show.ex:710 #, elixir-autogen, elixir-format msgid "Expires:" msgstr "Истекает:" -#: lib/who_need_help_web/live/request_live/new.ex:272 +#: lib/who_need_help_web/live/request_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Explain what is already arranged and what the volunteer needs to do." msgstr "Объясните, что уже сделано и что нужно сделать волонтеру." -#: lib/who_need_help_web/live/activity_live/index.ex:110 +#: lib/who_need_help_web/live/activity_live/index.ex:179 #, elixir-autogen, elixir-format msgid "Find people to join" msgstr "Найти компанию" -#: lib/who_need_help_web/live/activity_live/show.ex:645 -#: lib/who_need_help_web/live/moderation_live.ex:246 -#: lib/who_need_help_web/live/request_live/show.ex:975 +#: lib/who_need_help_web/live/activity_live/show.ex:664 +#: lib/who_need_help_web/live/moderation_live.ex:250 +#: lib/who_need_help_web/live/request_live/show.ex:1088 #, elixir-autogen, elixir-format msgid "Fraud" msgstr "Мошенничество" @@ -842,41 +809,41 @@ msgstr "Мошенничество" msgid "Free help and thanks" msgstr "Бесплатная помощь и благодарность" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:51 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:62 #, elixir-autogen, elixir-format msgid "GitHub profile verified." msgstr "Профиль GitHub подтверждён." -#: lib/who_need_help_web/live/profile_live.ex:250 +#: lib/who_need_help_web/live/profile_live.ex:253 #, elixir-autogen, elixir-format msgid "GitHub verification is unavailable until the operator configures its OAuth credentials. No manually entered link is treated as verified." msgstr "" "Подтверждение через GitHub недоступно, пока оператор не настроит учётные " "данные OAuth. Ссылка, введённая вручную, не считается подтверждённой." -#: lib/who_need_help_web/live/request_live/show.ex:864 +#: lib/who_need_help_web/live/request_live/show.ex:975 #, elixir-autogen, elixir-format msgid "HANDOVER CODE FOR THE HELPER" msgstr "КОД ПЕРЕДАЧИ ДЛЯ ПОМОЩНИКА" -#: lib/who_need_help_web/live/profile_live.ex:319 +#: lib/who_need_help_web/live/profile_live.ex:322 #, elixir-autogen, elixir-format msgid "Handle (optional)" msgstr "Имя пользователя (необязательно)" -#: lib/who_need_help_web/live/request_live/show.ex:122 +#: lib/who_need_help_web/live/request_live/show.ex:147 #, elixir-autogen, elixir-format msgid "Handover code verified." msgstr "Код передачи подтверждён." -#: lib/who_need_help_web/live/activity_live/show.ex:643 -#: lib/who_need_help_web/live/moderation_live.ex:245 -#: lib/who_need_help_web/live/request_live/show.ex:974 +#: lib/who_need_help_web/live/activity_live/show.ex:662 +#: lib/who_need_help_web/live/moderation_live.ex:249 +#: lib/who_need_help_web/live/request_live/show.ex:1087 #, elixir-autogen, elixir-format msgid "Harassment" msgstr "Домогательства" -#: lib/who_need_help_web/live/request_live/show.ex:110 +#: lib/who_need_help_web/live/request_live/show.ex:135 #, elixir-autogen, elixir-format msgid "Help is in progress." msgstr "Помощь выполняется." @@ -895,12 +862,12 @@ msgstr "" msgid "Helper" msgstr "Помощник" -#: lib/who_need_help_web/live/request_live/show.ex:767 +#: lib/who_need_help_web/live/request_live/show.ex:878 #, elixir-autogen, elixir-format msgid "Helper: %{name}" msgstr "Помощник: %{name}" -#: lib/who_need_help_web/components/layouts.ex:64 +#: lib/who_need_help_web/components/layouts.ex:176 #, elixir-autogen, elixir-format msgid "Helpers" msgstr "Помощники" @@ -946,26 +913,26 @@ msgstr "" "\n" "Если вы не запрашивали это письмо, просто проигнорируйте его." -#: lib/who_need_help_web/live/activity_live/new.ex:257 -#: lib/who_need_help_web/live/profile_live.ex:163 -#: lib/who_need_help_web/live/request_live/new.ex:313 -#: lib/who_need_help_web/live/request_live/show.ex:542 +#: lib/who_need_help_web/live/activity_live/new.ex:259 +#: lib/who_need_help_web/live/profile_live.ex:166 +#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/show.ex:640 #, elixir-autogen, elixir-format msgid "Hidden" msgstr "Скрыто" -#: lib/who_need_help_web/live/moderation_live.ex:346 +#: lib/who_need_help_web/live/moderation_live.ex:350 #, elixir-autogen, elixir-format msgid "Hide activity" msgstr "Скрыть активность" -#: lib/who_need_help_web/live/moderation_live.ex:332 -#: lib/who_need_help_web/live/moderation_live.ex:344 +#: lib/who_need_help_web/live/moderation_live.ex:336 +#: lib/who_need_help_web/live/moderation_live.ex:348 #, elixir-autogen, elixir-format msgid "Hide reason" msgstr "Причина скрытия" -#: lib/who_need_help_web/live/moderation_live.ex:334 +#: lib/who_need_help_web/live/moderation_live.ex:338 #, elixir-autogen, elixir-format msgid "Hide request" msgstr "Скрыть заявку" @@ -975,18 +942,18 @@ msgstr "Скрыть заявку" msgid "Human decisions retained" msgstr "Решения остаются за человеком" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48 #, elixir-autogen, elixir-format msgid "I am 18 or older and accept the safety rules" msgstr "Мне 18 лет и я принимаю правила безопасности." #: lib/who_need_help_web/controllers/page_html/home.html.heex:56 -#: lib/who_need_help_web/live/request_live/show.ex:751 +#: lib/who_need_help_web/live/request_live/show.ex:862 #, elixir-autogen, elixir-format msgid "I can help" msgstr "Я могу помочь" -#: lib/who_need_help_web/live/request_live/new.ex:124 +#: lib/who_need_help_web/live/request_live/new.ex:127 #, elixir-autogen, elixir-format msgid "I confirm there is no immediate danger, people and vehicles are away from active traffic or emergency services have already been contacted, and the requested task is lawful." msgstr "" @@ -994,7 +961,7 @@ msgstr "" "находятся вне активного движения либо экстренные службы уже вызваны, а " "запрашиваемая помощь законна." -#: lib/who_need_help_web/live/request_live/new.ex:118 +#: lib/who_need_help_web/live/request_live/new.ex:121 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, medical advice request, controlled substance, or request to buy medicine on my behalf." msgstr "" @@ -1002,14 +969,14 @@ msgstr "" "консультации или контролируемого вещества и не просьба купить лекарство от " "моего имени." -#: lib/who_need_help_web/live/request_live/new.ex:130 +#: lib/who_need_help_web/live/request_live/new.ex:133 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, there is no immediate danger, and the requested task is lawful." msgstr "" "Я подтверждаю, что это не экстренная ситуация, непосредственной опасности " "нет, а запрашиваемая помощь законна." -#: lib/who_need_help_web/live/activity_live/new.ex:292 +#: lib/who_need_help_web/live/activity_live/new.ex:298 #, elixir-autogen, elixir-format msgid "I will approve participants individually, keep sensitive details in the approved group, and use an appropriate public first meeting point." msgstr "" @@ -1022,21 +989,21 @@ msgstr "" msgid "Identity and reputation" msgstr "Личность и репутация" -#: lib/who_need_help_web/controllers/user_session_controller.ex:66 +#: lib/who_need_help_web/controllers/user_session_controller.ex:89 #, elixir-autogen, elixir-format msgid "If your email is in our system, you will receive instructions for logging in shortly." msgstr "" "Если этот адрес электронной почты есть в нашей системе, вскоре вы получите " "инструкции для входа." -#: lib/who_need_help_web/live/activity_live/show.ex:647 -#: lib/who_need_help_web/live/moderation_live.ex:249 -#: lib/who_need_help_web/live/request_live/show.ex:978 +#: lib/who_need_help_web/live/activity_live/show.ex:666 +#: lib/who_need_help_web/live/moderation_live.ex:253 +#: lib/who_need_help_web/live/request_live/show.ex:1091 #, elixir-autogen, elixir-format msgid "Impersonation" msgstr "Выдача себя за другого человека" -#: lib/who_need_help_web/live/request_live/show.ex:916 +#: lib/who_need_help_web/live/request_live/show.ex:1027 #, elixir-autogen, elixir-format msgid "In a browser this works only while the page remains open. The Android app shows a persistent notification and can continue after it is minimized. Raw coordinates are removed when sharing stops." msgstr "" @@ -1044,78 +1011,73 @@ msgstr "" "Android показывает постоянное уведомление и может продолжать передачу после " "сворачивания. При остановке передачи исходные координаты удаляются." -#: lib/who_need_help_web/live/request_live/index.ex:106 -#: lib/who_need_help_web/live/request_live/index.ex:107 -#: lib/who_need_help_web/live/request_live/show.ex:536 +#: lib/who_need_help_web/live/request_live/index.ex:180 +#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/show.ex:634 #, elixir-autogen, elixir-format msgid "In progress" msgstr "В процессе" -#: lib/who_need_help_web/live/profile_live.ex:151 +#: lib/who_need_help_web/live/profile_live.ex:154 #, elixir-autogen, elixir-format msgid "Interface language" msgstr "Язык интерфейса" -#: lib/who_need_help_web/live/moderation_live.ex:617 +#: lib/who_need_help_web/live/moderation_live.ex:621 #, elixir-autogen, elixir-format msgid "Internal note" msgstr "Внутренняя заметка" -#: lib/who_need_help_web/controllers/user_session_controller.ex:47 +#: lib/who_need_help_web/controllers/user_session_controller.ex:124 #, elixir-autogen, elixir-format msgid "Invalid email or password" msgstr "Неверная электронная почта или пароль" -#: lib/who_need_help_web/live/activity_live/show.ex:301 +#: lib/who_need_help_web/live/activity_live/show.ex:314 #, elixir-autogen, elixir-format msgid "Join requests are closed." msgstr "Приём заявок на участие закрыт." -#: lib/who_need_help_web/live/activity_live/show.ex:361 +#: lib/who_need_help_web/live/activity_live/show.ex:380 #, elixir-autogen, elixir-format msgid "Join requests close" msgstr "Приём заявок завершается" -#: lib/who_need_help_web/live/activity_live/new.ex:237 +#: lib/who_need_help_web/live/activity_live/new.ex:239 #, elixir-autogen, elixir-format msgid "Join requests close (UTC)" msgstr "Завершение приёма заявок (UTC)" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:54 -#, elixir-autogen, elixir-format -msgid "Keep me logged in on this device" -msgstr "Не выходить из учётной записи на этом устройстве" - -#: lib/who_need_help_web/live/activity_live/new.ex:278 -#: lib/who_need_help_web/live/request_live/new.ex:336 +#: lib/who_need_help_web/live/activity_live/new.ex:282 +#: lib/who_need_help_web/live/request_live/new.ex:340 #, elixir-autogen, elixir-format msgid "Latitude" msgstr "Широта" -#: lib/who_need_help_web/live/activity_live/show.ex:515 +#: lib/who_need_help_web/live/activity_live/show.ex:534 #, elixir-autogen, elixir-format msgid "Leave activity" msgstr "Покинуть активность" -#: lib/who_need_help_web/live/request_live/show.ex:914 +#: lib/who_need_help_web/live/request_live/show.ex:1025 #, elixir-autogen, elixir-format msgid "Live location" msgstr "Геопозиция в реальном времени" #: lib/who_need_help_web/controllers/page_html/safety.html.heex:51 -#: lib/who_need_help_web/live/activity_live/show.ex:484 +#: lib/who_need_help_web/live/activity_live/show.ex:503 #, elixir-autogen, elixir-format msgid "Location privacy" msgstr "Конфиденциальность геопозиции" -#: lib/who_need_help_web/live/request_live/show.ex:803 +#: lib/who_need_help_web/live/request_live/show.ex:914 #, elixir-autogen, elixir-format msgid "Location sharing is optional; missing evidence is not proof of abuse." msgstr "" "Передача геопозиции необязательна; отсутствие таких данных не является " "доказательством злоупотребления." -#: lib/who_need_help_web/live/request_live/new.ex:310 +#: lib/who_need_help_web/live/request_live/new.ex:312 #, elixir-autogen, elixir-format msgid "Location visibility" msgstr "Видимость геопозиции" @@ -1125,12 +1087,12 @@ msgstr "Видимость геопозиции" msgid "Location-supported people" msgstr "Люди с подтверждением геопозицией" -#: lib/who_need_help_web/live/request_live/show.ex:609 +#: lib/who_need_help_web/live/request_live/show.ex:717 #, elixir-autogen, elixir-format msgid "Location:" msgstr "Геопозиция:" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:69 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102 #, elixir-autogen, elixir-format msgid "Log in and stay logged in" msgstr "Войти и оставаться в системе" @@ -1140,44 +1102,27 @@ msgstr "Войти и оставаться в системе" msgid "Log in instructions" msgstr "Инструкции для входа" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:72 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105 #, elixir-autogen, elixir-format msgid "Log in only this time" msgstr "Войти только сейчас" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:45 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78 #, elixir-autogen, elixir-format msgid "Log in with email" msgstr "Войти с email" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:60 -#, elixir-autogen, elixir-format -msgid "Log me in only this time" -msgstr "Войти только сейчас" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:102 +#: lib/who_need_help_web/controllers/user_session_controller.ex:117 #, elixir-autogen, elixir-format msgid "Logged out successfully." msgstr "Вы успешно вышли." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:42 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:51 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:57 -#, elixir-autogen, elixir-format -msgid "Logging in..." -msgstr "Вход…" - -#: lib/who_need_help_web/live/activity_live/new.ex:285 -#: lib/who_need_help_web/live/request_live/new.ex:343 +#: lib/who_need_help_web/live/activity_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/new.ex:347 #, elixir-autogen, elixir-format msgid "Longitude" msgstr "Долгота" -#: lib/who_need_help_web/controllers/user_session_controller.ex:95 -#, elixir-autogen, elixir-format -msgid "Magic link is invalid or it has expired." -msgstr "Ссылка для входа недействительна или просрочена." - #: lib/who_need_help_web/controllers/feedback_html/show.html.heex:12 #, elixir-autogen, elixir-format msgid "Main Codex session ID" @@ -1190,7 +1135,7 @@ msgstr "" "Управляйте адресом электронной почты вашей учетной записи и настройками " "пароля" -#: lib/who_need_help_web/live/profile_live.ex:232 +#: lib/who_need_help_web/live/profile_live.ex:235 #, elixir-autogen, elixir-format msgid "Manually added links are public and marked unverified. A verified badge is only granted after completing a provider authorization flow." msgstr "" @@ -1198,7 +1143,7 @@ msgstr "" "подтверждения появляется только после авторизации у соответствующего " "провайдера." -#: lib/who_need_help_web/live/activity_live/show.ex:572 +#: lib/who_need_help_web/live/activity_live/show.ex:591 #, elixir-autogen, elixir-format msgid "Mark completed" msgstr "Отметить завершённой" @@ -1208,14 +1153,14 @@ msgstr "Отметить завершённой" msgid "Match and coordinate" msgstr "Найдите помощника и согласуйте детали" -#: lib/who_need_help_web/live/request_live/index.ex:104 -#: lib/who_need_help_web/live/request_live/index.ex:105 -#: lib/who_need_help_web/live/request_live/show.ex:534 +#: lib/who_need_help_web/live/request_live/index.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/show.ex:632 #, elixir-autogen, elixir-format msgid "Matched" msgstr "Помощник найден" -#: lib/who_need_help_web/live/request_live/new.ex:110 +#: lib/who_need_help_web/live/request_live/new.ex:113 #, elixir-autogen, elixir-format msgid "Medicine is ready at the pharmacy" msgstr "Лекарство готово к получению в аптеке" @@ -1230,7 +1175,7 @@ msgstr "Заказ лекарств готов в аптеке" msgid "Medicine pickup" msgstr "Доставка лекарств" -#: lib/who_need_help_web/live/request_live/new.ex:154 +#: lib/who_need_help_web/live/request_live/new.ex:156 #, elixir-autogen, elixir-format msgid "Medicine pickup remains the first priority. Roadside categories use the same moderated, extensible request flow with details specific to each type of help." msgstr "" @@ -1243,7 +1188,7 @@ msgstr "" msgid "Medicine requests" msgstr "Заявки на лекарства" -#: lib/who_need_help_web/live/activity_live/index.ex:126 +#: lib/who_need_help_web/live/activity_live/index.ex:195 #, elixir-autogen, elixir-format msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgstr "" @@ -1251,27 +1196,27 @@ msgstr "" "одобренной группе и уходите или отправляйте жалобу, если ситуация кажется " "небезопасной." -#: lib/who_need_help_web/live/activity_live/show.ex:367 +#: lib/who_need_help_web/live/activity_live/show.ex:386 #, elixir-autogen, elixir-format msgid "Meeting area" msgstr "Место встречи" -#: lib/who_need_help_web/live/moderation_live.ex:548 +#: lib/who_need_help_web/live/moderation_live.ex:552 #, elixir-autogen, elixir-format msgid "Merge" msgstr "Объединить" -#: lib/who_need_help_web/live/moderation_live.ex:542 +#: lib/who_need_help_web/live/moderation_live.ex:546 #, elixir-autogen, elixir-format msgid "Merge into" msgstr "Объединить с" -#: lib/who_need_help_web/live/moderation_live.ex:547 +#: lib/who_need_help_web/live/moderation_live.ex:551 #, elixir-autogen, elixir-format msgid "Merge note" msgstr "Примечание к объединению" -#: lib/who_need_help_web/live/activity_live/show.ex:464 +#: lib/who_need_help_web/live/activity_live/show.ex:483 #, elixir-autogen, elixir-format msgid "Message the approved group" msgstr "Написать одобренной группе" @@ -1281,41 +1226,41 @@ msgstr "Написать одобренной группе" msgid "Mode" msgstr "Режим" -#: lib/who_need_help_web/components/layouts.ex:73 +#: lib/who_need_help_web/components/layouts.ex:181 #: lib/who_need_help_web/live/moderation_live.ex:9 -#: lib/who_need_help_web/live/moderation_live.ex:287 +#: lib/who_need_help_web/live/moderation_live.ex:291 #, elixir-autogen, elixir-format msgid "Moderation" msgstr "Модерация" -#: lib/who_need_help_web/live/moderation_live.ex:267 -#: lib/who_need_help_web/live/moderation_live.ex:633 +#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:637 #, elixir-autogen, elixir-format msgid "Moderator" msgstr "Модератор" -#: lib/who_need_help_web/live/moderation_live.ex:226 -#: lib/who_need_help_web/user_auth.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/user_auth.ex:306 #, elixir-autogen, elixir-format msgid "Moderator access is required." msgstr "Требуется доступ модератора." -#: lib/who_need_help_web/live/request_live/index.ex:122 +#: lib/who_need_help_web/live/request_live/index.ex:196 #, elixir-autogen, elixir-format msgid "NEARBY MUTUAL AID" msgstr "ВЗАИМОПОМОЩЬ РЯДОМ" -#: lib/who_need_help_web/live/request_live/index.ex:64 +#: lib/who_need_help_web/live/request_live/index.ex:97 #, elixir-autogen, elixir-format msgid "Nearby help" msgstr "Помощь рядом" -#: lib/who_need_help_web/live/request_live/new.ex:113 +#: lib/who_need_help_web/live/request_live/new.ex:116 #, elixir-autogen, elixir-format msgid "Need roadside help in a safe location" msgstr "Нужна помощь на дороге в безопасном месте" -#: lib/who_need_help_web/live/profile_live.ex:301 +#: lib/who_need_help_web/live/profile_live.ex:304 #, elixir-autogen, elixir-format msgid "Network" msgstr "Сеть" @@ -1325,10 +1270,10 @@ msgstr "Сеть" msgid "New password" msgstr "Новый пароль" -#: lib/who_need_help_web/live/activity_live/new.ex:205 -#: lib/who_need_help_web/live/activity_live/show.ex:283 -#: lib/who_need_help_web/live/request_live/new.ex:253 -#: lib/who_need_help_web/live/request_live/show.ex:472 +#: lib/who_need_help_web/live/activity_live/new.ex:207 +#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/request_live/new.ex:255 +#: lib/who_need_help_web/live/request_live/show.ex:563 #, elixir-autogen, elixir-format msgid "No" msgstr "Нет" @@ -1338,28 +1283,28 @@ msgstr "Нет" msgid "No completed help yet." msgstr "Завершённой помощи пока нет." -#: lib/who_need_help_web/live/activity_live/show.ex:432 -#: lib/who_need_help_web/live/request_live/show.ex:663 +#: lib/who_need_help_web/live/activity_live/show.ex:451 +#: lib/who_need_help_web/live/request_live/show.ex:771 #, elixir-autogen, elixir-format msgid "No messages yet." msgstr "Сообщений пока нет." -#: lib/who_need_help_web/live/activity_live/index.ex:157 +#: lib/who_need_help_web/live/activity_live/index.ex:227 #, elixir-autogen, elixir-format msgid "No open activities yet" msgstr "Открытых активностей пока нет" -#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/index.ex:254 #, elixir-autogen, elixir-format msgid "No open requests yet" msgstr "Открытых заявок пока нет" -#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:460 #, elixir-autogen, elixir-format msgid "No open signals." msgstr "Открытых сигналов нет." -#: lib/who_need_help_web/live/activity_live/show.ex:567 +#: lib/who_need_help_web/live/activity_live/show.ex:586 #, elixir-autogen, elixir-format msgid "No pending join requests." msgstr "Нет ожидающих заявок на участие." @@ -1369,17 +1314,17 @@ msgstr "Нет ожидающих заявок на участие." msgid "No proposals yet." msgstr "Предложений пока нет." -#: lib/who_need_help_web/live/moderation_live.ex:372 +#: lib/who_need_help_web/live/moderation_live.ex:376 #, elixir-autogen, elixir-format msgid "No reports." msgstr "Жалоб нет." -#: lib/who_need_help_web/live/profile_live.ex:258 +#: lib/who_need_help_web/live/profile_live.ex:261 #, elixir-autogen, elixir-format msgid "No social links added." msgstr "Ссылки на соцсети не добавлены." -#: lib/who_need_help_web/live/profile_live.ex:209 +#: lib/who_need_help_web/live/profile_live.ex:212 #, elixir-autogen, elixir-format msgid "None revealed yet." msgstr "Пока ничего не открыто." @@ -1396,22 +1341,22 @@ msgstr "" msgid "Not for emergencies" msgstr "Не для чрезвычайных ситуаций" -#: lib/who_need_help_web/live/request_live/index.ex:94 -#: lib/who_need_help_web/live/request_live/index.ex:95 #: lib/who_need_help_web/live/request_live/index.ex:168 -#: lib/who_need_help_web/live/request_live/new.ex:291 -#: lib/who_need_help_web/live/request_live/show.ex:525 -#: lib/who_need_help_web/live/request_live/show.ex:526 +#: lib/who_need_help_web/live/request_live/index.ex:169 +#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/new.ex:293 +#: lib/who_need_help_web/live/request_live/show.ex:623 +#: lib/who_need_help_web/live/request_live/show.ex:624 #, elixir-autogen, elixir-format msgid "Now" msgstr "Сейчас" -#: lib/who_need_help_web/live/activity_live/index.ex:108 +#: lib/who_need_help_web/live/activity_live/index.ex:177 #, elixir-autogen, elixir-format msgid "OPTIONAL SOCIAL PLANS" msgstr "НЕОБЯЗАТЕЛЬНЫЕ СОВМЕСТНЫЕ ПЛАНЫ" -#: lib/who_need_help_web/live/request_live/index.ex:125 +#: lib/who_need_help_web/live/request_live/index.ex:199 #, elixir-autogen, elixir-format msgid "Only approximate areas are shown before a safe match." msgstr "" @@ -1427,29 +1372,29 @@ msgstr "" "вещества, выдачу наличных, переупаковку или покупку от вашего имени. " "Соблюдайте правила аптеки и местное законодательство." -#: lib/who_need_help_web/live/moderation_live.ex:390 +#: lib/who_need_help_web/live/moderation_live.ex:394 #, elixir-autogen, elixir-format msgid "Only the conversation linked to this report is displayed." msgstr "Показана только переписка, связанная с этой жалобой." -#: lib/who_need_help_web/live/activity_live/show.ex:418 +#: lib/who_need_help_web/live/activity_live/show.ex:437 #, elixir-autogen, elixir-format msgid "Only the organizer and approved participants can read or send these messages." msgstr "" "Только организатор и одобренные участники могут читать или отправлять эти " "сообщения." -#: lib/who_need_help_web/live/activity_live/index.ex:97 -#: lib/who_need_help_web/live/activity_live/show.ex:319 -#: lib/who_need_help_web/live/moderation_live.ex:253 -#: lib/who_need_help_web/live/request_live/index.ex:102 -#: lib/who_need_help_web/live/request_live/index.ex:103 -#: lib/who_need_help_web/live/request_live/show.ex:533 +#: lib/who_need_help_web/live/activity_live/index.ex:166 +#: lib/who_need_help_web/live/activity_live/show.ex:338 +#: lib/who_need_help_web/live/moderation_live.ex:257 +#: lib/who_need_help_web/live/request_live/index.ex:176 +#: lib/who_need_help_web/live/request_live/index.ex:177 +#: lib/who_need_help_web/live/request_live/show.ex:631 #, elixir-autogen, elixir-format msgid "Open" msgstr "Открыта" -#: lib/who_need_help_web/live/moderation_live.ex:420 +#: lib/who_need_help_web/live/moderation_live.ex:424 #, elixir-autogen, elixir-format msgid "Open abuse signals" msgstr "Открытые сигналы о злоупотреблениях" @@ -1459,40 +1404,40 @@ msgstr "Открытые сигналы о злоупотреблениях" msgid "Open proposals" msgstr "Открытые предложения" -#: lib/who_need_help_web/live/profile_live.ex:171 +#: lib/who_need_help_web/live/profile_live.ex:174 #, elixir-autogen, elixir-format msgid "Optional external thank-you link" msgstr "Необязательная внешняя ссылка для благодарности" -#: lib/who_need_help_web/live/request_live/show.ex:900 +#: lib/who_need_help_web/live/request_live/show.ex:1011 #, elixir-autogen, elixir-format msgid "Optional thank-you link" msgstr "Необязательная ссылка для благодарности" -#: lib/who_need_help_web/live/activity_live/show.ex:371 +#: lib/who_need_help_web/live/activity_live/show.ex:390 #, elixir-autogen, elixir-format msgid "Organizer" msgstr "Организатор" -#: lib/who_need_help_web/live/activity_live/show.ex:160 +#: lib/who_need_help_web/live/activity_live/show.ex:155 #, elixir-autogen, elixir-format msgid "Organizer blocked. Their activities and messages are hidden." msgstr "Организатор заблокирован. Его активности и сообщения скрыты." -#: lib/who_need_help_web/live/activity_live/show.ex:520 +#: lib/who_need_help_web/live/activity_live/show.ex:539 #, elixir-autogen, elixir-format msgid "Organizer controls" msgstr "Управление организатора" -#: lib/who_need_help_web/live/activity_live/show.ex:380 +#: lib/who_need_help_web/live/activity_live/show.ex:399 #, elixir-autogen, elixir-format msgid "Organizer social links" msgstr "Ссылки организатора на соцсети" -#: lib/who_need_help_web/live/activity_live/show.ex:648 -#: lib/who_need_help_web/live/moderation_live.ex:250 -#: lib/who_need_help_web/live/profile_live.ex:308 -#: lib/who_need_help_web/live/request_live/show.ex:979 +#: lib/who_need_help_web/live/activity_live/show.ex:667 +#: lib/who_need_help_web/live/moderation_live.ex:254 +#: lib/who_need_help_web/live/profile_live.ex:311 +#: lib/who_need_help_web/live/request_live/show.ex:1092 #, elixir-autogen, elixir-format msgid "Other" msgstr "Другое" @@ -1502,57 +1447,57 @@ msgstr "Другое" msgid "Parent (optional)" msgstr "Родительская категория (необязательно)" -#: lib/who_need_help_web/live/activity_live/show.ex:440 +#: lib/who_need_help_web/live/activity_live/show.ex:459 #, elixir-autogen, elixir-format msgid "Participant" msgstr "Участник" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:64 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97 #, elixir-autogen, elixir-format msgid "Password" msgstr "Пароль" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:47 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 #, elixir-autogen, elixir-format msgid "Password updated successfully." msgstr "Пароль успешно обновлён." -#: lib/who_need_help_web/live/request_live/new.ex:278 +#: lib/who_need_help_web/live/request_live/new.ex:280 #, elixir-autogen, elixir-format msgid "Pickup instructions (optional)" msgstr "Инструкции для получения (необязательно)" -#: lib/who_need_help_web/live/request_live/show.ex:578 +#: lib/who_need_help_web/live/request_live/show.ex:686 #, elixir-autogen, elixir-format msgid "Pickup notes" msgstr "Инструкции по получению" -#: lib/who_need_help_web/live/activity_live/new.ex:223 +#: lib/who_need_help_web/live/activity_live/new.ex:225 #, elixir-autogen, elixir-format msgid "Plan and expectations" msgstr "План и ожидания" -#: lib/who_need_help_web/live/activity_live/show.ex:402 +#: lib/who_need_help_web/live/activity_live/show.ex:421 #, elixir-autogen, elixir-format msgid "Plan details" msgstr "Подробности плана" -#: lib/who_need_help_web/live/request_live/show.ex:520 +#: lib/who_need_help_web/live/request_live/show.ex:619 #, elixir-autogen, elixir-format msgid "Please check the entered data." msgstr "Пожалуйста, проверьте введенные данные." -#: lib/who_need_help_web/live/moderation_live.ex:235 +#: lib/who_need_help_web/live/moderation_live.ex:240 #, elixir-autogen, elixir-format msgid "Please check the submitted fields." msgstr "Пожалуйста, проверьте представленные поля." -#: lib/who_need_help_web/live/activity_live/show.ex:314 +#: lib/who_need_help_web/live/activity_live/show.ex:334 #, elixir-autogen, elixir-format msgid "Please check the submitted message." msgstr "Пожалуйста, проверьте отправленное сообщение." -#: lib/who_need_help_web/live/request_live/new.ex:305 +#: lib/who_need_help_web/live/request_live/new.ex:307 #, elixir-autogen, elixir-format msgid "Podil, near the central pharmacy" msgstr "Подол, рядом с центральной аптекой" @@ -1562,12 +1507,13 @@ msgstr "Подол, рядом с центральной аптекой" msgid "Post a clear request" msgstr "Опубликуйте понятную заявку" -#: lib/who_need_help_web/components/layouts.ex:45 +#: lib/who_need_help_web/components/layouts.ex:55 +#: lib/who_need_help_web/components/layouts.ex:87 #, elixir-autogen, elixir-format msgid "Primary navigation" msgstr "Основная навигация" -#: lib/who_need_help_web/live/request_live/show.ex:647 +#: lib/who_need_help_web/live/request_live/show.ex:755 #, elixir-autogen, elixir-format msgid "Private match chat" msgstr "Приватный чат участников" @@ -1584,8 +1530,8 @@ msgstr "" msgid "Profile updated." msgstr "Профиль обновлён." -#: lib/who_need_help_web/live/moderation_live.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:976 +#: lib/who_need_help_web/live/moderation_live.ex:251 +#: lib/who_need_help_web/live/request_live/show.ex:1089 #, elixir-autogen, elixir-format msgid "Prohibited item" msgstr "Запрещённый предмет" @@ -1629,29 +1575,29 @@ msgstr "" "одним участником другого; производные признаки движения и близости могут " "сохраняться." -#: lib/who_need_help_web/live/activity_live/new.ex:254 +#: lib/who_need_help_web/live/activity_live/new.ex:256 #, elixir-autogen, elixir-format msgid "Public map visibility" msgstr "Видимость на публичной карте" -#: lib/who_need_help_web/live/profile_live.ex:314 +#: lib/who_need_help_web/live/profile_live.ex:317 #, elixir-autogen, elixir-format msgid "Public profile URL" msgstr "URL публичного профиля" -#: lib/who_need_help_web/live/profile_live.ex:182 +#: lib/who_need_help_web/live/profile_live.ex:185 #, elixir-autogen, elixir-format msgid "Public reputation" msgstr "Публичная репутация" -#: lib/who_need_help_web/live/activity_live/new.ex:115 +#: lib/who_need_help_web/live/activity_live/new.ex:117 #, elixir-autogen, elixir-format msgid "Publish a clear public plan. Join requests stay pending until you approve them." msgstr "" "Опубликуйте понятный публичный план. Заявки на участие будут ожидать, пока " "вы их не одобрите." -#: lib/who_need_help_web/live/activity_live/new.ex:301 +#: lib/who_need_help_web/live/activity_live/new.ex:310 #, elixir-autogen, elixir-format msgid "Publish activity" msgstr "Опубликовать активность" @@ -1661,13 +1607,13 @@ msgstr "Опубликовать активность" msgid "Publish proposal" msgstr "Опубликовать предложение" -#: lib/who_need_help_web/live/request_live/new.ex:355 +#: lib/who_need_help_web/live/request_live/new.ex:364 #, elixir-autogen, elixir-format msgid "Publish request" msgstr "Опубликовать заявку" -#: lib/who_need_help_web/live/activity_live/new.ex:299 -#: lib/who_need_help_web/live/request_live/new.ex:353 +#: lib/who_need_help_web/live/activity_live/new.ex:308 +#: lib/who_need_help_web/live/request_live/new.ex:362 #, elixir-autogen, elixir-format msgid "Publishing…" msgstr "Публикация…" @@ -1677,7 +1623,7 @@ msgstr "Публикация…" msgid "READ BEFORE USING THE SERVICE" msgstr "ПРОЧИТАЙТЕ ПЕРЕД ИСПОЛЬЗОВАНИЕМ СЕРВИСА" -#: lib/who_need_help_web/live/moderation_live.ex:286 +#: lib/who_need_help_web/live/moderation_live.ex:290 #, elixir-autogen, elixir-format msgid "RESTRICTED WORKSPACE" msgstr "ОГРАНИЧЕННАЯ РАБОЧАЯ ОБЛАСТЬ" @@ -1697,17 +1643,17 @@ msgstr "" "показатель." #: lib/who_need_help_web/live/leaderboard_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:712 +#: lib/who_need_help_web/live/request_live/show.ex:823 #, elixir-autogen, elixir-format msgid "Rating" msgstr "Оценка" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40 #, elixir-autogen, elixir-format msgid "Read the" msgstr "Прочитайте" -#: lib/who_need_help_web/live/activity_live/show.ex:641 +#: lib/who_need_help_web/live/activity_live/show.ex:660 #, elixir-autogen, elixir-format msgid "Reason" msgstr "Причина" @@ -1717,38 +1663,38 @@ msgstr "Причина" msgid "Register for an account" msgstr "Зарегистрировать учётную запись" -#: lib/who_need_help_web/live/moderation_live.ex:531 +#: lib/who_need_help_web/live/moderation_live.ex:535 #, elixir-autogen, elixir-format msgid "Reject" msgstr "Отклонить" -#: lib/who_need_help_web/live/moderation_live.ex:530 +#: lib/who_need_help_web/live/moderation_live.ex:534 #, elixir-autogen, elixir-format msgid "Rejection note" msgstr "Примечание об отклонении" -#: lib/who_need_help_web/live/profile_live.ex:292 +#: lib/who_need_help_web/live/profile_live.ex:295 #, elixir-autogen, elixir-format msgid "Remove" msgstr "Удалить" -#: lib/who_need_help_web/live/activity_live/show.ex:449 +#: lib/who_need_help_web/live/activity_live/show.ex:468 #, elixir-autogen, elixir-format msgid "Report" msgstr "Пожаловаться" -#: lib/who_need_help_web/live/request_live/show.ex:681 +#: lib/who_need_help_web/live/request_live/show.ex:789 #, elixir-autogen, elixir-format msgid "Report message" msgstr "Пожаловаться на сообщение" -#: lib/who_need_help_web/live/request_live/show.ex:971 +#: lib/who_need_help_web/live/request_live/show.ex:1084 #, elixir-autogen, elixir-format msgid "Report reason" msgstr "Причина жалобы" -#: lib/who_need_help_web/live/activity_live/show.ex:128 -#: lib/who_need_help_web/live/request_live/show.ex:231 +#: lib/who_need_help_web/live/activity_live/show.ex:123 +#: lib/who_need_help_web/live/request_live/show.ex:261 #, elixir-autogen, elixir-format msgid "Report sent to moderators." msgstr "Жалоба отправлена модераторам." @@ -1758,29 +1704,29 @@ msgstr "Жалоба отправлена модераторам." msgid "Report updated." msgstr "Жалоба обновлена." -#: lib/who_need_help_web/live/moderation_live.ex:386 +#: lib/who_need_help_web/live/moderation_live.ex:390 #, elixir-autogen, elixir-format msgid "Reported evidence" msgstr "Материалы жалобы" -#: lib/who_need_help_web/live/moderation_live.ex:293 +#: lib/who_need_help_web/live/moderation_live.ex:297 #, elixir-autogen, elixir-format msgid "Reports" msgstr "Жалобы" -#: lib/who_need_help_web/live/request_live/show.ex:940 +#: lib/who_need_help_web/live/request_live/show.ex:1053 #, elixir-autogen, elixir-format msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages." msgstr "" "Жалобы поступают модераторам. Блокировка скрывает заявки в обоих " "направлениях и не позволяет отправлять новые сообщения." -#: lib/who_need_help_web/live/request_live/show.ex:102 +#: lib/who_need_help_web/live/request_live/show.ex:127 #, elixir-autogen, elixir-format msgid "Request cancelled." msgstr "Заявка отменена." -#: lib/who_need_help_web/live/request_live/new.ex:299 +#: lib/who_need_help_web/live/request_live/new.ex:301 #, elixir-autogen, elixir-format msgid "Request expires (UTC)" msgstr "Срок действия заявки (UTC)" @@ -1795,83 +1741,83 @@ msgstr "Заявка скрыта." msgid "Request restored." msgstr "Заявка восстановлена." -#: lib/who_need_help_web/live/activity_live/show.ex:502 +#: lib/who_need_help_web/live/activity_live/show.ex:521 #, elixir-autogen, elixir-format msgid "Request to join" msgstr "Подать заявку на участие" -#: lib/who_need_help_web/live/request_live/show.ex:617 +#: lib/who_need_help_web/live/request_live/show.ex:725 #, elixir-autogen, elixir-format msgid "Requester links:" msgstr "Ссылки заявителя:" -#: lib/who_need_help_web/live/request_live/show.ex:598 +#: lib/who_need_help_web/live/request_live/show.ex:706 #, elixir-autogen, elixir-format msgid "Requester:" msgstr "Заявитель:" -#: lib/who_need_help_web/live/request_live/show.ex:632 +#: lib/who_need_help_web/live/request_live/show.ex:740 #, elixir-autogen, elixir-format msgid "Requester: %{count} completed" msgstr "Автор заявки: %{count} завершено" -#: lib/who_need_help_web/live/moderation_live.ex:367 +#: lib/who_need_help_web/live/moderation_live.ex:371 #, elixir-autogen, elixir-format msgid "Resolution note" msgstr "Примечание к решению" -#: lib/who_need_help_web/live/moderation_live.ex:255 -#: lib/who_need_help_web/live/moderation_live.ex:361 +#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:365 #, elixir-autogen, elixir-format msgid "Resolved" msgstr "Решено" -#: lib/who_need_help_web/live/moderation_live.ex:262 -#: lib/who_need_help_web/live/moderation_live.ex:611 +#: lib/who_need_help_web/live/moderation_live.ex:266 +#: lib/who_need_help_web/live/moderation_live.ex:615 #, elixir-autogen, elixir-format msgid "Restricted" msgstr "Ограничен" -#: lib/who_need_help_web/live/moderation_live.ex:451 +#: lib/who_need_help_web/live/moderation_live.ex:455 #, elixir-autogen, elixir-format msgid "Review note" msgstr "Примечание к проверке" -#: lib/who_need_help_web/live/request_live/show.ex:203 +#: lib/who_need_help_web/live/request_live/show.ex:233 #, elixir-autogen, elixir-format msgid "Review saved. It appears after both participants review." msgstr "" "Отзыв сохранён. Он появится после того, как отзывы оставят оба участника." -#: lib/who_need_help_web/live/moderation_live.ex:257 -#: lib/who_need_help_web/live/moderation_live.ex:447 +#: lib/who_need_help_web/live/moderation_live.ex:261 +#: lib/who_need_help_web/live/moderation_live.ex:451 #, elixir-autogen, elixir-format msgid "Reviewed" msgstr "Проверено" -#: lib/who_need_help_web/live/moderation_live.ex:254 -#: lib/who_need_help_web/live/moderation_live.ex:360 +#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:364 #, elixir-autogen, elixir-format msgid "Reviewing" msgstr "На рассмотрении" -#: lib/who_need_help_web/live/moderation_live.ex:571 +#: lib/who_need_help_web/live/moderation_live.ex:575 #, elixir-autogen, elixir-format msgid "Role" msgstr "Роль" -#: lib/who_need_help_web/live/moderation_live.ex:510 +#: lib/who_need_help_web/live/moderation_live.ex:514 #, elixir-autogen, elixir-format msgid "Russian description (optional)" msgstr "Описание на русском (необязательно)" -#: lib/who_need_help_web/live/moderation_live.ex:498 +#: lib/who_need_help_web/live/moderation_live.ex:502 #, elixir-autogen, elixir-format msgid "Russian name (optional)" msgstr "Название на русском (необязательно)" -#: lib/who_need_help_web/live/activity_live/show.ex:616 -#: lib/who_need_help_web/live/request_live/show.ex:938 +#: lib/who_need_help_web/live/activity_live/show.ex:635 +#: lib/who_need_help_web/live/request_live/show.ex:1051 #, elixir-autogen, elixir-format msgid "Safety controls" msgstr "Инструменты безопасности" @@ -1884,15 +1830,15 @@ msgstr "" "публичный запуск и каждое изменение production-инфраструктуры остаются под " "контролем людей." -#: lib/who_need_help_web/components/layouts.ex:104 +#: lib/who_need_help_web/components/layouts.ex:145 #: lib/who_need_help_web/controllers/page_html/safety.html.heex:6 #, elixir-autogen, elixir-format msgid "Safety rules" msgstr "Правила безопасности" -#: lib/who_need_help_web/live/moderation_live.ex:369 -#: lib/who_need_help_web/live/moderation_live.ex:452 -#: lib/who_need_help_web/live/moderation_live.ex:619 +#: lib/who_need_help_web/live/moderation_live.ex:373 +#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:623 #, elixir-autogen, elixir-format msgid "Save" msgstr "Сохранить" @@ -1902,29 +1848,29 @@ msgstr "Сохранить" msgid "Save Password" msgstr "Сохранить пароль" -#: lib/who_need_help_web/live/profile_live.ex:177 +#: lib/who_need_help_web/live/profile_live.ex:180 #, elixir-autogen, elixir-format msgid "Save profile" msgstr "Сохранить профиль" -#: lib/who_need_help_web/live/request_live/index.ex:98 -#: lib/who_need_help_web/live/request_live/index.ex:99 -#: lib/who_need_help_web/live/request_live/index.ex:170 -#: lib/who_need_help_web/live/request_live/new.ex:293 -#: lib/who_need_help_web/live/request_live/show.ex:529 -#: lib/who_need_help_web/live/request_live/show.ex:530 +#: lib/who_need_help_web/live/request_live/index.ex:172 +#: lib/who_need_help_web/live/request_live/index.ex:173 +#: lib/who_need_help_web/live/request_live/index.ex:245 +#: lib/who_need_help_web/live/request_live/new.ex:295 +#: lib/who_need_help_web/live/request_live/show.ex:627 +#: lib/who_need_help_web/live/request_live/show.ex:628 #, elixir-autogen, elixir-format msgid "Scheduled" msgstr "Запланировано" -#: lib/who_need_help_web/live/activity_live/show.ex:467 -#: lib/who_need_help_web/live/request_live/show.ex:696 +#: lib/who_need_help_web/live/activity_live/show.ex:486 +#: lib/who_need_help_web/live/request_live/show.ex:806 #, elixir-autogen, elixir-format msgid "Send" msgstr "Отправить" -#: lib/who_need_help_web/live/activity_live/show.ex:657 -#: lib/who_need_help_web/live/request_live/show.ex:988 +#: lib/who_need_help_web/live/activity_live/show.ex:676 +#: lib/who_need_help_web/live/request_live/show.ex:1101 #, elixir-autogen, elixir-format msgid "Send report" msgstr "Отправить жалобу" @@ -1936,30 +1882,25 @@ msgstr "" "Установите CODEX_SESSION_ID в среде развёртывания перед записью " "демонстрации." -#: lib/who_need_help_web/live/request_live/show.ex:926 +#: lib/who_need_help_web/live/request_live/show.ex:1038 #, elixir-autogen, elixir-format msgid "Share location" msgstr "Поделиться геопозицией" -#: lib/who_need_help_web/live/request_live/new.ex:280 +#: lib/who_need_help_web/live/request_live/new.ex:282 #, elixir-autogen, elixir-format msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat." msgstr "" "Сообщайте только нечувствительные инструкции. Точные сведения можно передать" " в чате после назначения помощника." -#: lib/who_need_help_web/live/request_live/show.ex:501 -#, elixir-autogen, elixir-format -msgid "Shared live location" -msgstr "Передаваемая геопозиция" - -#: lib/who_need_help_web/live/profile_live.ex:147 +#: lib/who_need_help_web/live/profile_live.ex:150 #, elixir-autogen, elixir-format msgid "Short bio" msgstr "Кратко о себе" -#: lib/who_need_help_web/live/activity_live/new.ex:217 -#: lib/who_need_help_web/live/request_live/new.ex:264 +#: lib/who_need_help_web/live/activity_live/new.ex:219 +#: lib/who_need_help_web/live/request_live/new.ex:266 #, elixir-autogen, elixir-format msgid "Short title" msgstr "Краткий заголовок" @@ -1974,12 +1915,12 @@ msgstr "Зарегистрироваться" msgid "Signal updated." msgstr "Сигнал обновлен." -#: lib/who_need_help_web/components/layouts/root.html.heex:52 +#: lib/who_need_help_web/components/layouts/root.html.heex:27 #, elixir-autogen, elixir-format msgid "Skip to main content" msgstr "Пропустить основной контент" -#: lib/who_need_help_web/live/moderation_live.ex:490 +#: lib/who_need_help_web/live/moderation_live.ex:494 #, elixir-autogen, elixir-format msgid "Slug" msgstr "Слаг" @@ -2000,12 +1941,12 @@ msgstr "Совместная активность" msgid "Social link added as unverified." msgstr "Ссылка на соцсеть добавлена как неподтверждённая." -#: lib/who_need_help_web/live/profile_live.ex:67 +#: lib/who_need_help_web/live/profile_live.ex:74 #, elixir-autogen, elixir-format msgid "Social link was not found." msgstr "Ссылка на соцсеть не найдена." -#: lib/who_need_help_web/live/profile_live.ex:230 +#: lib/who_need_help_web/live/profile_live.ex:233 #, elixir-autogen, elixir-format msgid "Social links" msgstr "Ссылки на соцсети" @@ -2015,54 +1956,54 @@ msgstr "Ссылки на соцсети" msgid "Social verification could not be started." msgstr "Не удалось начать подтверждение соцсети." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:77 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:88 #, elixir-autogen, elixir-format msgid "Social verification failed. Please retry." msgstr "Не удалось подтвердить соцсеть. Попробуйте ещё раз." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:58 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 #, elixir-autogen, elixir-format msgid "Social verification session expired. Please retry." msgstr "Сеанс подтверждения соцсети завершился. Попробуйте ещё раз." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:65 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:76 #, elixir-autogen, elixir-format msgid "Social verification session is invalid." msgstr "Сеанс социальной проверки недействителен." -#: lib/who_need_help_web/live/activity_live/show.ex:646 -#: lib/who_need_help_web/live/moderation_live.ex:248 -#: lib/who_need_help_web/live/request_live/show.ex:977 +#: lib/who_need_help_web/live/activity_live/show.ex:665 +#: lib/who_need_help_web/live/moderation_live.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:1090 #, elixir-autogen, elixir-format msgid "Spam" msgstr "Спам" -#: lib/who_need_help_web/live/request_live/show.ex:829 +#: lib/who_need_help_web/live/request_live/show.ex:940 #, elixir-autogen, elixir-format msgid "Start helping" msgstr "Начать помогать" -#: lib/who_need_help_web/live/activity_live/show.ex:355 +#: lib/who_need_help_web/live/activity_live/show.ex:374 #, elixir-autogen, elixir-format msgid "Starts" msgstr "Начало" -#: lib/who_need_help_web/live/activity_live/new.ex:232 +#: lib/who_need_help_web/live/activity_live/new.ex:234 #, elixir-autogen, elixir-format msgid "Starts at (UTC)" msgstr "Начало (UTC)" -#: lib/who_need_help_web/live/moderation_live.ex:572 +#: lib/who_need_help_web/live/moderation_live.ex:576 #, elixir-autogen, elixir-format msgid "Status" msgstr "Статус" -#: lib/who_need_help_web/live/request_live/show.ex:933 +#: lib/who_need_help_web/live/request_live/show.ex:1046 #, elixir-autogen, elixir-format msgid "Stop and delete position" msgstr "Остановить и удалить геопозицию" -#: lib/who_need_help_web/live/moderation_live.ex:515 +#: lib/who_need_help_web/live/moderation_live.ex:519 #, elixir-autogen, elixir-format msgid "Structured fields JSON" msgstr "Структурированные поля JSON" @@ -2072,34 +2013,34 @@ msgstr "Структурированные поля JSON" msgid "Structured fields must be a valid JSON object." msgstr "Структурированные поля должны быть действительным объектом JSON." -#: lib/who_need_help_web/live/request_live/show.ex:726 +#: lib/who_need_help_web/live/request_live/show.ex:837 #, elixir-autogen, elixir-format msgid "Submit review" msgstr "Отправить отзыв" -#: lib/who_need_help_web/live/moderation_live.ex:263 -#: lib/who_need_help_web/live/moderation_live.ex:612 +#: lib/who_need_help_web/live/moderation_live.ex:267 +#: lib/who_need_help_web/live/moderation_live.ex:616 #, elixir-autogen, elixir-format msgid "Suspended" msgstr "Заблокирован" -#: lib/who_need_help_web/live/request_live/show.ex:509 +#: lib/who_need_help_web/live/request_live/show.ex:604 #, elixir-autogen, elixir-format msgid "That handover code is not correct." msgstr "Неверный код передачи." -#: lib/who_need_help_web/live/activity_live/show.ex:145 -#: lib/who_need_help_web/live/request_live/show.ex:248 +#: lib/who_need_help_web/live/activity_live/show.ex:140 +#: lib/who_need_help_web/live/request_live/show.ex:278 #, elixir-autogen, elixir-format msgid "That message cannot be reported from this view." msgstr "На это сообщение нельзя пожаловаться с этого экрана." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:80 #, elixir-autogen, elixir-format msgid "That social account is already linked." msgstr "Эта учётная запись соцсети уже привязана." -#: lib/who_need_help_web/live/request_live/show.ex:513 +#: lib/who_need_help_web/live/request_live/show.ex:608 #, elixir-autogen, elixir-format msgid "That step is not available in the current state." msgstr "Этот шаг недоступен в текущем состоянии." @@ -2114,27 +2055,27 @@ msgstr "Провайдер подтверждения не настроен." msgid "That verification provider is not supported." msgstr "Этот провайдер проверки не поддерживается." -#: lib/who_need_help_web/live/activity_live/show.ex:299 +#: lib/who_need_help_web/live/activity_live/show.ex:312 #, elixir-autogen, elixir-format msgid "The approved group has reached its capacity." msgstr "Одобренная группа уже заполнена." -#: lib/who_need_help_web/live/request_live/show.ex:180 +#: lib/who_need_help_web/live/request_live/show.ex:208 #, elixir-autogen, elixir-format msgid "The browser could not share your location." msgstr "Браузеру не удалось передать вашу геопозицию." -#: lib/who_need_help_web/live/moderation_live.ex:233 +#: lib/who_need_help_web/live/moderation_live.ex:237 #, elixir-autogen, elixir-format msgid "The last administrator cannot demote themselves." msgstr "Последний администратор не может понизить собственную роль." -#: lib/who_need_help_web/controllers/user_session_controller.ex:31 +#: lib/who_need_help_web/controllers/user_session_controller.ex:37 #, elixir-autogen, elixir-format msgid "The link is invalid or it has expired." msgstr "Ссылка недействительна или просрочена." -#: lib/who_need_help_web/live/activity_live/show.ex:507 +#: lib/who_need_help_web/live/activity_live/show.ex:526 #, elixir-autogen, elixir-format msgid "The organizer has not approved your request yet." msgstr "Организатор ещё не одобрил вашу заявку." @@ -2145,59 +2086,59 @@ msgid "The pharmacy closes soon and I cannot leave home. The item is already pai msgstr "" "Аптека скоро закрывается, а я не могу выйти из дома. Товар уже оплачен." -#: lib/who_need_help_web/live/profile_live.ex:175 +#: lib/who_need_help_web/live/profile_live.ex:178 #, elixir-autogen, elixir-format msgid "The platform never handles, requires, splits, or guarantees a payment." msgstr "" "Платформа никогда не обрабатывает, не требует, не разделяет и не гарантирует" " оплату." -#: lib/who_need_help_web/live/activity_live/show.ex:489 +#: lib/who_need_help_web/live/activity_live/show.ex:508 #, elixir-autogen, elixir-format msgid "The public map is approximate or hidden. Exact coordinates appear only after approval." msgstr "" "Публичная карта показывает приблизительную область либо скрыта. Точные " "координаты появляются только после одобрения." -#: lib/who_need_help_web/live/activity_live/show.ex:629 +#: lib/who_need_help_web/live/activity_live/show.ex:648 #, elixir-autogen, elixir-format msgid "The report is scoped to the selected group message." msgstr "Жалоба относится к выбранному сообщению группы." -#: lib/who_need_help_web/live/request_live/show.ex:963 +#: lib/who_need_help_web/live/request_live/show.ex:1076 #, elixir-autogen, elixir-format msgid "The report will be scoped to the selected chat message." msgstr "Жалоба будет относиться к выбранному сообщению чата." -#: lib/who_need_help_web/live/activity_live/show.ex:302 +#: lib/who_need_help_web/live/activity_live/show.ex:315 #, elixir-autogen, elixir-format msgid "This action is unavailable." msgstr "Это действие недоступно." -#: lib/who_need_help_web/live/activity_live/show.ex:309 +#: lib/who_need_help_web/live/activity_live/show.ex:322 #, elixir-autogen, elixir-format msgid "This activity is no longer open." msgstr "Эта активность больше не открыта." -#: lib/who_need_help_web/live/activity_live/show.ex:307 +#: lib/who_need_help_web/live/activity_live/show.ex:320 #, elixir-autogen, elixir-format msgid "This activity state has already changed." msgstr "Состояние этой активности уже изменилось." -#: lib/who_need_help_web/live/request_live/show.ex:518 +#: lib/who_need_help_web/live/request_live/show.ex:613 #, elixir-autogen, elixir-format msgid "This interaction is blocked." msgstr "Это взаимодействие заблокировано." -#: lib/who_need_help_web/live/request_live/index.ex:136 -#: lib/who_need_help_web/live/request_live/show.ex:557 +#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/request_live/show.ex:665 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgstr "" "Это не экстренная служба. При непосредственной опасности обратитесь в " "местные экстренные службы." -#: lib/who_need_help_web/live/request_live/new.ex:162 +#: lib/who_need_help_web/live/request_live/new.ex:164 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger. Never include prescription details, payment card data, access codes, or other sensitive information." msgstr "" @@ -2205,7 +2146,7 @@ msgstr "" "местные экстренные службы. Никогда не указывайте данные рецепта или " "платёжной карты, коды доступа и другую чувствительную информацию." -#: lib/who_need_help_web/live/moderation_live.ex:227 +#: lib/who_need_help_web/live/moderation_live.ex:231 #, elixir-autogen, elixir-format msgid "This proposal has already been reviewed." msgstr "Это предложение уже было рассмотрено." @@ -2215,67 +2156,62 @@ msgstr "Это предложение уже было рассмотрено." msgid "This proposal is already closed." msgstr "Это предложение уже закрыто." -#: lib/who_need_help_web/live/moderation_live.ex:406 +#: lib/who_need_help_web/live/moderation_live.ex:410 #, elixir-autogen, elixir-format msgid "This report has no linked conversation." msgstr "У этой жалобы нет связанной переписки." -#: lib/who_need_help_web/live/request_live/show.ex:905 +#: lib/who_need_help_web/live/request_live/show.ex:1016 #, elixir-autogen, elixir-format msgid "This request already has a helper." msgstr "У этой заявки уже есть помощник." -#: lib/who_need_help_web/live/request_live/show.ex:52 +#: lib/who_need_help_web/live/request_live/show.ex:74 +#: lib/who_need_help_web/live/request_live/show.ex:615 #, elixir-autogen, elixir-format msgid "This request is no longer available." msgstr "Эта заявка больше недоступна." -#: lib/who_need_help_web/live/request_live/show.ex:37 +#: lib/who_need_help_web/live/request_live/show.ex:59 #, elixir-autogen, elixir-format msgid "This request is unavailable." msgstr "Эта заявка недоступна." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:66 -#, elixir-autogen, elixir-format -msgid "Tip: If you prefer passwords, you can enable them in the user settings." -msgstr "" -"Совет: если вам удобнее пользоваться паролем, его можно включить в " -"настройках." - #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:27 #, elixir-autogen, elixir-format msgid "To see sent emails, visit" msgstr "Чтобы посмотреть отправленные письма, откройте" -#: lib/who_need_help_web/live/request_live/index.ex:96 -#: lib/who_need_help_web/live/request_live/index.ex:97 -#: lib/who_need_help_web/live/request_live/index.ex:169 -#: lib/who_need_help_web/live/request_live/new.ex:292 -#: lib/who_need_help_web/live/request_live/show.ex:527 -#: lib/who_need_help_web/live/request_live/show.ex:528 +#: lib/who_need_help_web/live/request_live/index.ex:170 +#: lib/who_need_help_web/live/request_live/index.ex:171 +#: lib/who_need_help_web/live/request_live/index.ex:244 +#: lib/who_need_help_web/live/request_live/new.ex:294 +#: lib/who_need_help_web/live/request_live/show.ex:625 +#: lib/who_need_help_web/live/request_live/show.ex:626 #, elixir-autogen, elixir-format msgid "Today" msgstr "Сегодня" -#: lib/who_need_help_web/live/activity_live/new.ex:100 -#: lib/who_need_help_web/live/activity_live/show.ex:312 +#: lib/who_need_help_web/live/activity_live/new.ex:103 +#: lib/who_need_help_web/live/activity_live/show.ex:332 #: lib/who_need_help_web/live/category_proposal_live.ex:54 -#: lib/who_need_help_web/live/request_live/show.ex:519 +#: lib/who_need_help_web/live/profile_live.ex:59 +#: lib/who_need_help_web/live/request_live/show.ex:618 #, elixir-autogen, elixir-format msgid "Too many actions in the configured time window." msgstr "Слишком много действий за настроенный промежуток времени." -#: lib/who_need_help_web/controllers/user_registration_controller.ex:45 +#: lib/who_need_help_web/controllers/user_registration_controller.ex:49 #, elixir-autogen, elixir-format msgid "Too many registration attempts in the configured time window." msgstr "Слишком много попыток регистрации за настроенный промежуток времени." -#: lib/who_need_help_web/live/request_live/new.ex:139 +#: lib/who_need_help_web/live/request_live/new.ex:142 #, elixir-autogen, elixir-format msgid "Too many requests in the configured time window." msgstr "Слишком много запросов за настроенный промежуток времени." -#: lib/who_need_help_web/controllers/user_session_controller.ex:79 +#: lib/who_need_help_web/controllers/user_session_controller.ex:102 #, elixir-autogen, elixir-format msgid "Too many sign-in emails in the configured time window." msgstr "Слишком много писем для входа за настроенный промежуток времени." @@ -2290,27 +2226,27 @@ msgstr "Верхний уровень" msgid "Total" msgstr "Всего" -#: lib/who_need_help_web/live/activity_live/new.ex:244 +#: lib/who_need_help_web/live/activity_live/new.ex:246 #, elixir-autogen, elixir-format msgid "Total group capacity" msgstr "Общий размер группы" -#: lib/who_need_help_web/live/moderation_live.ex:506 +#: lib/who_need_help_web/live/moderation_live.ex:510 #, elixir-autogen, elixir-format msgid "Ukrainian description (optional)" msgstr "Описание на украинском (необязательно)" -#: lib/who_need_help_web/live/moderation_live.ex:494 +#: lib/who_need_help_web/live/moderation_live.ex:498 #, elixir-autogen, elixir-format msgid "Ukrainian name (optional)" msgstr "Название на украинском (необязательно)" -#: lib/who_need_help_web/live/profile_live.ex:342 +#: lib/who_need_help_web/live/profile_live.ex:345 #, elixir-autogen, elixir-format msgid "Unblock" msgstr "Разблокировать" -#: lib/who_need_help_web/live/request_live/show.ex:956 +#: lib/who_need_help_web/live/request_live/show.ex:1069 #, elixir-autogen, elixir-format msgid "Unblock this user" msgstr "Разблокировать этого пользователя" @@ -2325,8 +2261,8 @@ msgstr "Уникальные люди" msgid "Update email instructions" msgstr "Инструкции для изменения электронной почты" -#: lib/who_need_help_web/live/request_live/index.ex:165 -#: lib/who_need_help_web/live/request_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/index.ex:240 +#: lib/who_need_help_web/live/request_live/new.ex:291 #, elixir-autogen, elixir-format msgid "Urgency" msgstr "Срочность" @@ -2337,7 +2273,7 @@ msgstr "Срочность" msgid "Urgent help" msgstr "Срочная помощь" -#: lib/who_need_help_web/live/activity_live/new.ex:121 +#: lib/who_need_help_web/live/activity_live/new.ex:123 #, elixir-autogen, elixir-format msgid "Use a public first meeting point. Do not publish a home address, access code, travel document, or other sensitive information." msgstr "" @@ -2345,18 +2281,18 @@ msgstr "" "адрес, код доступа, документ для поездки или другую чувствительную " "информацию." -#: lib/who_need_help_web/components/layouts.ex:194 +#: lib/who_need_help_web/components/layouts.ex:349 #, elixir-autogen, elixir-format msgid "Use dark theme" msgstr "Включить тёмную тему" -#: lib/who_need_help_web/components/layouts.ex:185 +#: lib/who_need_help_web/components/layouts.ex:340 #, elixir-autogen, elixir-format msgid "Use light theme" msgstr "Включить светлую тему" -#: lib/who_need_help_web/live/activity_live/new.ex:267 -#: lib/who_need_help_web/live/request_live/new.ex:325 +#: lib/who_need_help_web/live/activity_live/new.ex:271 +#: lib/who_need_help_web/live/request_live/new.ex:329 #, elixir-autogen, elixir-format msgid "Use my current foreground location" msgstr "Использовать мою текущую геопозицию" @@ -2368,24 +2304,24 @@ msgstr "" "Используйте приватный чат и, по желанию, передачу геопозиции в реальном " "времени." -#: lib/who_need_help_web/components/layouts.ex:176 +#: lib/who_need_help_web/components/layouts.ex:331 #, elixir-autogen, elixir-format msgid "Use system theme" msgstr "Использовать системную тему" -#: lib/who_need_help_web/live/moderation_live.ex:266 -#: lib/who_need_help_web/live/moderation_live.ex:570 -#: lib/who_need_help_web/live/moderation_live.ex:632 +#: lib/who_need_help_web/live/moderation_live.ex:270 +#: lib/who_need_help_web/live/moderation_live.ex:574 +#: lib/who_need_help_web/live/moderation_live.ex:636 #, elixir-autogen, elixir-format msgid "User" msgstr "Пользователь" -#: lib/who_need_help_web/live/request_live/show.ex:271 +#: lib/who_need_help_web/live/request_live/show.ex:307 #, elixir-autogen, elixir-format msgid "User blocked. Their requests and new messages are hidden." msgstr "Пользователь заблокирован. Его заявки и новые сообщения скрыты." -#: lib/who_need_help_web/controllers/user_session_controller.ex:19 +#: lib/who_need_help_web/controllers/user_session_controller.ex:25 #, elixir-autogen, elixir-format msgid "User confirmed successfully." msgstr "Пользователь успешно подтверждён." @@ -2400,8 +2336,8 @@ msgstr "Обновлена роль пользователя." msgid "User status updated." msgstr "Обновлен статус пользователя." -#: lib/who_need_help_web/live/profile_live.ex:80 -#: lib/who_need_help_web/live/request_live/show.ex:285 +#: lib/who_need_help_web/live/profile_live.ex:87 +#: lib/who_need_help_web/live/request_live/show.ex:321 #, elixir-autogen, elixir-format msgid "User unblocked." msgstr "Пользователь разблокирован." @@ -2411,12 +2347,12 @@ msgstr "Пользователь разблокирован." msgid "Verified people" msgstr "Подтверждённые люди" -#: lib/who_need_help_web/live/profile_live.ex:242 +#: lib/who_need_help_web/live/profile_live.ex:245 #, elixir-autogen, elixir-format msgid "Verify GitHub" msgstr "Подтвердить через GitHub" -#: lib/who_need_help_web/live/request_live/show.ex:885 +#: lib/who_need_help_web/live/request_live/show.ex:996 #, elixir-autogen, elixir-format msgid "Verify handover" msgstr "Подтвердить передачу" @@ -2426,28 +2362,23 @@ msgstr "Подтвердить передачу" msgid "Verify the handover" msgstr "Подтвердите передачу" -#: lib/who_need_help_web/live/moderation_live.ex:323 +#: lib/who_need_help_web/live/moderation_live.ex:327 #, elixir-autogen, elixir-format msgid "View scoped evidence" msgstr "Просмотреть связанные доказательства" -#: lib/who_need_help_web/live/profile_live.ex:207 +#: lib/who_need_help_web/live/profile_live.ex:210 #, elixir-autogen, elixir-format msgid "Visible reviews" msgstr "Открытые отзывы" -#: lib/who_need_help_web/live/request_live/show.ex:755 +#: lib/who_need_help_web/live/request_live/show.ex:866 #, elixir-autogen, elixir-format msgid "Waiting for a nearby volunteer." msgstr "Ожидаем помощника поблизости." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:4 -#, elixir-autogen, elixir-format -msgid "Welcome %{email}" -msgstr "Добро пожаловать %{email}" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:20 -#: lib/who_need_help_web/controllers/user_session_controller.ex:40 +#: lib/who_need_help_web/controllers/user_session_controller.ex:26 +#: lib/who_need_help_web/controllers/user_session_controller.ex:51 #, elixir-autogen, elixir-format msgid "Welcome back!" msgstr "С возвращением!" @@ -2457,8 +2388,8 @@ msgstr "С возвращением!" msgid "What Codex contributed" msgstr "Что сделал Codex" -#: lib/who_need_help_web/live/activity_live/show.ex:654 -#: lib/who_need_help_web/live/request_live/show.ex:985 +#: lib/who_need_help_web/live/activity_live/show.ex:673 +#: lib/who_need_help_web/live/request_live/show.ex:1098 #, elixir-autogen, elixir-format msgid "What happened?" msgstr "Что случилось?" @@ -2468,7 +2399,7 @@ msgstr "Что случилось?" msgid "What help category is missing?" msgstr "Какая категория помощи отсутствует?" -#: lib/who_need_help_web/live/request_live/new.ex:270 +#: lib/who_need_help_web/live/request_live/new.ex:272 #, elixir-autogen, elixir-format msgid "What help do you need?" msgstr "Какая помощь вам нужна?" @@ -2489,12 +2420,12 @@ msgstr "" "может проверить каждого человека, каждую заявку, вещь, маршрут или результат" " и не может гарантировать безопасность." -#: lib/who_need_help_web/components/layouts.ex:40 +#: lib/who_need_help_web/components/layouts.ex:43 #, elixir-autogen, elixir-format msgid "Who Need Help home" msgstr "Главная Who Need Help" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:54 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53 #, elixir-autogen, elixir-format msgid "Who Need Help is not an emergency or medical service. Medicine requests are limited to pickup of legal items already purchased or reserved." msgstr "" @@ -2510,7 +2441,7 @@ msgstr "" "через подписку ChatGPT разработчика. Ключ OpenAI API и резервный вариант API" " с оплатой по использованию не применяются." -#: lib/who_need_help_web/live/request_live/index.ex:123 +#: lib/who_need_help_web/live/request_live/index.ex:197 #, elixir-autogen, elixir-format msgid "Who needs help?" msgstr "Кому нужна помощь?" @@ -2520,47 +2451,47 @@ msgstr "Кому нужна помощь?" msgid "Why is this useful?" msgstr "Почему это полезно?" -#: lib/who_need_help_web/live/request_live/show.ex:846 +#: lib/who_need_help_web/live/request_live/show.ex:957 #, elixir-autogen, elixir-format msgid "Withdraw from this request" msgstr "Отказаться от этой заявки" -#: lib/who_need_help_web/live/request_live/show.ex:693 +#: lib/who_need_help_web/live/request_live/show.ex:802 #, elixir-autogen, elixir-format msgid "Write a safe coordination message…" msgstr "Напишите безопасное сообщение для координации…" -#: lib/who_need_help_web/live/activity_live/new.ex:202 -#: lib/who_need_help_web/live/activity_live/show.ex:281 -#: lib/who_need_help_web/live/request_live/new.ex:250 -#: lib/who_need_help_web/live/request_live/show.ex:470 +#: lib/who_need_help_web/live/activity_live/new.ex:204 +#: lib/who_need_help_web/live/activity_live/show.ex:294 +#: lib/who_need_help_web/live/request_live/new.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:561 #, elixir-autogen, elixir-format msgid "Yes" msgstr "Да" -#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/activity_live/show.ex:309 #, elixir-autogen, elixir-format msgid "You already have a join request for this activity." msgstr "Вы уже подали заявку на участие в этой активности." -#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#: lib/who_need_help_web/live/category_proposal_live.ex:58 #, elixir-autogen, elixir-format msgid "You already voted for this proposal." msgstr "Вы уже проголосовали за это предложение." -#: lib/who_need_help_web/live/activity_live/show.ex:487 +#: lib/who_need_help_web/live/activity_live/show.ex:506 #, elixir-autogen, elixir-format msgid "You are approved, so the map can show the exact meeting coordinates." msgstr "" "Вы одобрены, поэтому на карте могут быть указаны точные координаты встречи." -#: lib/who_need_help_web/live/request_live/show.ex:92 +#: lib/who_need_help_web/live/request_live/show.ex:117 #, elixir-autogen, elixir-format msgid "You are matched. Use chat to coordinate safely." msgstr "Помощник назначен. Безопасно согласуйте детали в чате." -#: lib/who_need_help_web/live/activity_live/show.ex:304 -#: lib/who_need_help_web/live/request_live/show.ex:510 +#: lib/who_need_help_web/live/activity_live/show.ex:317 +#: lib/who_need_help_web/live/request_live/show.ex:605 #, elixir-autogen, elixir-format msgid "You are not allowed to do that." msgstr "У вас нет разрешения на это действие." @@ -2570,45 +2501,45 @@ msgstr "У вас нет разрешения на это действие." msgid "You are running the local mail adapter." msgstr "Вы используете локальный почтовый адаптер." -#: lib/who_need_help_web/live/request_live/new.ex:328 +#: lib/who_need_help_web/live/request_live/new.ex:332 #, elixir-autogen, elixir-format msgid "You can also enter coordinates manually for local testing." msgstr "" "Вы также можете вручную ввести координаты для локального тестирования." -#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/index.ex:256 #, elixir-autogen, elixir-format msgid "You can be the first person to ask the community." msgstr "Вы можете первыми обратиться за помощью к сообществу." -#: lib/who_need_help_web/live/request_live/show.ex:507 +#: lib/who_need_help_web/live/request_live/show.ex:602 #, elixir-autogen, elixir-format msgid "You cannot accept your own request." msgstr "Нельзя принять собственную заявку." -#: lib/who_need_help_web/live/activity_live/show.ex:303 +#: lib/who_need_help_web/live/activity_live/show.ex:316 #, elixir-autogen, elixir-format msgid "You cannot report your own content." msgstr "Нельзя жаловаться на собственный контент." -#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/live/moderation_live.ex:234 #, elixir-autogen, elixir-format msgid "You cannot restrict your own moderator account." msgstr "Вы не можете ограничить свою учетную запись модератора." -#: lib/who_need_help_web/live/profile_live.ex:330 +#: lib/who_need_help_web/live/profile_live.ex:333 #, elixir-autogen, elixir-format msgid "You have not blocked anyone." msgstr "Вы никого не блокировали." -#: lib/who_need_help_web/user_auth.ex:208 -#: lib/who_need_help_web/user_auth.ex:240 -#: lib/who_need_help_web/user_auth.ex:270 +#: lib/who_need_help_web/user_auth.ex:256 +#: lib/who_need_help_web/user_auth.ex:288 +#: lib/who_need_help_web/user_auth.ex:318 #, elixir-autogen, elixir-format msgid "You must log in to access this page." msgstr "Вы должны войти, чтобы получить доступ к этой странице." -#: lib/who_need_help_web/user_auth.ex:178 +#: lib/who_need_help_web/user_auth.ex:226 #, elixir-autogen, elixir-format msgid "You must re-authenticate to access this page." msgstr "" @@ -2622,22 +2553,22 @@ msgstr "" "Чтобы выполнять чувствительные действия с учётной записью, нужно повторно " "войти." -#: lib/who_need_help_web/live/request_live/show.ex:117 +#: lib/who_need_help_web/live/request_live/show.ex:142 #, elixir-autogen, elixir-format msgid "You withdrew from this request." msgstr "Вы отказались помогать с этой заявкой." -#: lib/who_need_help_web/live/activity_live/index.ex:221 +#: lib/who_need_help_web/live/activity_live/index.ex:292 #, elixir-autogen, elixir-format msgid "Your activities" msgstr "Ваши активности" -#: lib/who_need_help_web/live/request_live/show.ex:113 +#: lib/who_need_help_web/live/request_live/show.ex:138 #, elixir-autogen, elixir-format msgid "Your confirmation was saved." msgstr "Ваше подтверждение сохранено." -#: lib/who_need_help_web/live/profile_live.ex:139 +#: lib/who_need_help_web/live/profile_live.ex:142 #, elixir-autogen, elixir-format msgid "Your profile" msgstr "Ваш профиль" @@ -2647,53 +2578,53 @@ msgstr "Ваш профиль" msgid "Your request is now visible to nearby helpers." msgstr "Теперь вашу заявку видят помощники рядом." -#: lib/who_need_help_web/live/request_live/index.ex:266 +#: lib/who_need_help_web/live/request_live/index.ex:342 #, elixir-autogen, elixir-format msgid "Your requests" msgstr "Ваши запросы" -#: lib/who_need_help_web/live/request_live/show.ex:706 +#: lib/who_need_help_web/live/request_live/show.ex:817 #, elixir-autogen, elixir-format msgid "Your review is revealed only after both participants submit." msgstr "" "Ваш отзыв откроется только после того, как его отправят оба участника." -#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/index.ex:319 #, elixir-autogen, elixir-format msgid "Your trust summary" msgstr "Ваши показатели доверия" -#: lib/who_need_help_web/live/moderation_live.ex:307 +#: lib/who_need_help_web/live/moderation_live.ex:311 #, elixir-autogen, elixir-format msgid "activity" msgstr "активность" -#: lib/who_need_help_web/live/moderation_live.ex:310 +#: lib/who_need_help_web/live/moderation_live.ex:314 #, elixir-autogen, elixir-format msgid "activity message" msgstr "сообщение активности" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 #, elixir-autogen, elixir-format msgid "before confirming." msgstr "перед подтверждением." -#: lib/who_need_help_web/live/activity_live/index.ex:190 +#: lib/who_need_help_web/live/activity_live/index.ex:261 #: lib/who_need_help_web/live/category_proposal_live.ex:147 -#: lib/who_need_help_web/live/moderation_live.ex:313 -#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/moderation_live.ex:317 +#: lib/who_need_help_web/live/request_live/index.ex:286 #, elixir-autogen, elixir-format msgid "by %{name}" msgstr "автор: %{name}" -#: lib/who_need_help_web/live/profile_live.ex:186 -#: lib/who_need_help_web/live/request_live/index.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:772 +#: lib/who_need_help_web/live/profile_live.ex:189 +#: lib/who_need_help_web/live/request_live/index.ex:323 +#: lib/who_need_help_web/live/request_live/show.ex:883 #, elixir-autogen, elixir-format msgid "completed" msgstr "завершено" -#: lib/who_need_help_web/live/request_live/index.ex:215 +#: lib/who_need_help_web/live/request_live/index.ex:291 #, elixir-autogen, elixir-format msgid "expires %{time}" msgstr "истекает %{time}" @@ -2703,59 +2634,59 @@ msgstr "истекает %{time}" msgid "for an account now." msgstr "учётную запись сейчас." -#: lib/who_need_help_web/live/request_live/show.ex:799 +#: lib/who_need_help_web/live/request_live/show.ex:910 #, elixir-autogen, elixir-format msgid "helper movement supported" msgstr "движение помощника подтверждено" -#: lib/who_need_help_web/live/request_live/show.ex:800 +#: lib/who_need_help_web/live/request_live/show.ex:911 #, elixir-autogen, elixir-format msgid "no movement evidence" msgstr "нет подтверждения движения" -#: lib/who_need_help_web/live/request_live/show.ex:791 +#: lib/who_need_help_web/live/request_live/show.ex:902 #, elixir-autogen, elixir-format msgid "no proximity evidence" msgstr "нет подтверждения близости" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82 #, elixir-autogen, elixir-format msgid "or" msgstr "или" -#: lib/who_need_help_web/live/activity_live/show.ex:586 +#: lib/who_need_help_web/live/activity_live/show.ex:605 #, elixir-autogen, elixir-format msgid "organizer" msgstr "организатор" -#: lib/who_need_help_web/live/request_live/show.ex:648 +#: lib/who_need_help_web/live/request_live/show.ex:756 #, elixir-autogen, elixir-format msgid "participants only" msgstr "только участники" -#: lib/who_need_help_web/live/request_live/index.ex:252 -#: lib/who_need_help_web/live/request_live/show.ex:776 +#: lib/who_need_help_web/live/request_live/index.ex:328 +#: lib/who_need_help_web/live/request_live/show.ex:887 #, elixir-autogen, elixir-format msgid "people" msgstr "люди" -#: lib/who_need_help_web/live/request_live/show.ex:790 +#: lib/who_need_help_web/live/request_live/show.ex:901 #, elixir-autogen, elixir-format msgid "proximity supported" msgstr "близость подтверждена" -#: lib/who_need_help_web/live/profile_live.ex:201 -#: lib/who_need_help_web/live/request_live/show.ex:780 +#: lib/who_need_help_web/live/profile_live.ex:204 +#: lib/who_need_help_web/live/request_live/show.ex:891 #, elixir-autogen, elixir-format msgid "rating" msgstr "оценка" -#: lib/who_need_help_web/live/request_live/show.ex:640 +#: lib/who_need_help_web/live/request_live/show.ex:748 #, elixir-autogen, elixir-format msgid "rating %{rating}" msgstr "оценка %{rating}" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 #, elixir-autogen, elixir-format msgid "safety rules" msgstr "Правила безопасности" @@ -2770,17 +2701,17 @@ msgstr "страница почтового ящика" msgid "to your account now." msgstr "в свою учётную запись сейчас." -#: lib/who_need_help_web/live/profile_live.ex:191 +#: lib/who_need_help_web/live/profile_live.ex:194 #, elixir-autogen, elixir-format msgid "unique people" msgstr "Уникальные люди" -#: lib/who_need_help_web/live/activity_live/show.ex:395 -#: lib/who_need_help_web/live/activity_live/show.ex:546 -#: lib/who_need_help_web/live/activity_live/show.ex:605 -#: lib/who_need_help_web/live/profile_live.ex:274 -#: lib/who_need_help_web/live/request_live/show.ex:627 -#: lib/who_need_help_web/live/request_live/show.ex:821 +#: lib/who_need_help_web/live/activity_live/show.ex:414 +#: lib/who_need_help_web/live/activity_live/show.ex:565 +#: lib/who_need_help_web/live/activity_live/show.ex:624 +#: lib/who_need_help_web/live/profile_live.ex:277 +#: lib/who_need_help_web/live/request_live/show.ex:735 +#: lib/who_need_help_web/live/request_live/show.ex:932 #, elixir-autogen, elixir-format msgid "unverified" msgstr "не подтверждено" @@ -2790,34 +2721,34 @@ msgstr "не подтверждено" msgid "urgent" msgstr "срочно" -#: lib/who_need_help_web/live/activity_live/show.ex:394 -#: lib/who_need_help_web/live/activity_live/show.ex:545 -#: lib/who_need_help_web/live/activity_live/show.ex:604 -#: lib/who_need_help_web/live/profile_live.ex:273 -#: lib/who_need_help_web/live/request_live/index.ex:257 -#: lib/who_need_help_web/live/request_live/show.ex:626 -#: lib/who_need_help_web/live/request_live/show.ex:820 +#: lib/who_need_help_web/live/activity_live/show.ex:413 +#: lib/who_need_help_web/live/activity_live/show.ex:564 +#: lib/who_need_help_web/live/activity_live/show.ex:623 +#: lib/who_need_help_web/live/profile_live.ex:276 +#: lib/who_need_help_web/live/request_live/index.ex:333 +#: lib/who_need_help_web/live/request_live/show.ex:734 +#: lib/who_need_help_web/live/request_live/show.ex:931 #, elixir-autogen, elixir-format msgid "verified" msgstr "подтверждено" -#: lib/who_need_help_web/live/profile_live.ex:196 +#: lib/who_need_help_web/live/profile_live.ex:199 #, elixir-autogen, elixir-format msgid "verified handovers" msgstr "подтверждённые передачи" -#: lib/who_need_help_web/live/request_live/show.ex:552 +#: lib/who_need_help_web/live/request_live/show.ex:660 #, elixir-autogen, elixir-format msgid "← All requests" msgstr "← Все заявки" -#: lib/who_need_help_web/live/activity_live/new.ex:111 -#: lib/who_need_help_web/live/activity_live/show.ex:329 +#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/show.ex:348 #, elixir-autogen, elixir-format msgid "← Back to activities" msgstr "← Назад к активностям" -#: lib/who_need_help_web/live/request_live/new.ex:150 +#: lib/who_need_help_web/live/request_live/new.ex:152 #, elixir-autogen, elixir-format msgid "← Back to requests" msgstr "← Назад к заявкам" @@ -2827,83 +2758,221 @@ msgstr "← Назад к заявкам" msgid "← Requests" msgstr "← Заявки" -#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:262 #, elixir-autogen, elixir-format msgid "Approved" msgstr "Одобрено" -#: lib/who_need_help_web/live/request_live/index.ex:112 -#: lib/who_need_help_web/live/request_live/index.ex:113 +#: lib/who_need_help_web/live/request_live/index.ex:186 +#: lib/who_need_help_web/live/request_live/index.ex:187 #, elixir-autogen, elixir-format msgid "Expired" msgstr "Истекло" -#: lib/who_need_help_web/live/moderation_live.ex:275 +#: lib/who_need_help_web/live/moderation_live.ex:279 #, elixir-autogen, elixir-format msgid "Handover without location evidence" msgstr "Передача без подтверждения геолокацией" -#: lib/who_need_help_web/live/moderation_live.ex:277 +#: lib/who_need_help_web/live/moderation_live.ex:281 #, elixir-autogen, elixir-format msgid "Location without movement" msgstr "Геолокация без движения" -#: lib/who_need_help_web/live/moderation_live.ex:279 +#: lib/who_need_help_web/live/moderation_live.ex:283 #, elixir-autogen, elixir-format msgid "Manual review" msgstr "Ручная проверка" -#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:264 #, elixir-autogen, elixir-format msgid "Merged" msgstr "Объединено" -#: lib/who_need_help_web/live/moderation_live.ex:278 +#: lib/who_need_help_web/live/moderation_live.ex:282 #, elixir-autogen, elixir-format msgid "Reciprocal rating ring" msgstr "Кольцо взаимных оценок" -#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:263 #, elixir-autogen, elixir-format msgid "Rejected" msgstr "Отклонено" -#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:276 #, elixir-autogen, elixir-format msgid "Repeated participant pair" msgstr "Повторяющаяся пара участников" -#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:275 #, elixir-autogen, elixir-format msgid "Unusual action rate" msgstr "Необычная частота действий" -#: lib/who_need_help_web/live/activity_live/show.ex:429 -#: lib/who_need_help_web/live/request_live/show.ex:660 +#: lib/who_need_help_web/live/activity_live/show.ex:448 +#: lib/who_need_help_web/live/request_live/show.ex:768 #, elixir-autogen, elixir-format msgid "Load earlier messages" msgstr "Загрузить предыдущие сообщения" -#: lib/who_need_help_web/live/activity_live/index.ex:202 -#: lib/who_need_help_web/live/activity_live/index.ex:238 +#: lib/who_need_help_web/live/activity_live/index.ex:273 +#: lib/who_need_help_web/live/activity_live/index.ex:310 #: lib/who_need_help_web/live/category_proposal_live.ex:162 #: lib/who_need_help_web/live/leaderboard_live.ex:87 -#: lib/who_need_help_web/live/moderation_live.ex:380 -#: lib/who_need_help_web/live/moderation_live.ex:415 -#: lib/who_need_help_web/live/moderation_live.ex:560 -#: lib/who_need_help_web/live/moderation_live.ex:652 -#: lib/who_need_help_web/live/profile_live.ex:221 -#: lib/who_need_help_web/live/profile_live.ex:351 -#: lib/who_need_help_web/live/request_live/index.ex:227 -#: lib/who_need_help_web/live/request_live/index.ex:283 +#: lib/who_need_help_web/live/moderation_live.ex:384 +#: lib/who_need_help_web/live/moderation_live.ex:419 +#: lib/who_need_help_web/live/moderation_live.ex:564 +#: lib/who_need_help_web/live/moderation_live.ex:656 +#: lib/who_need_help_web/live/profile_live.ex:224 +#: lib/who_need_help_web/live/profile_live.ex:354 +#: lib/who_need_help_web/live/request_live/index.ex:303 +#: lib/who_need_help_web/live/request_live/index.ex:360 #, elixir-autogen, elixir-format msgid "Load more" msgstr "Загрузить ещё" -#: lib/who_need_help_web/live/activity_live/index.ex:213 -#: lib/who_need_help_web/live/activity_live/show.ex:479 -#: lib/who_need_help_web/live/request_live/index.ex:238 -#: lib/who_need_help_web/live/request_live/show.ex:738 +#: lib/who_need_help_web/live/activity_live/index.ex:284 +#: lib/who_need_help_web/live/activity_live/show.ex:498 +#: lib/who_need_help_web/live/request_live/index.ex:314 +#: lib/who_need_help_web/live/request_live/show.ex:849 #, elixir-autogen, elixir-format msgid "The map is unavailable in this browser. Request details remain usable." msgstr "Карта недоступна в этом браузере. Остальная информация по-прежнему доступна." + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:4 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Confirm email change" +msgstr "Подтвердить изменение email" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:5 +#, elixir-autogen, elixir-format +msgid "Confirm the new email address for your account." +msgstr "Подтвердите новый email для своей учётной записи." + +#: lib/who_need_help_web/live/moderation_live.ex:242 +#, elixir-autogen, elixir-format +msgid "Could not complete moderation. Please try again." +msgstr "Не удалось завершить модерацию. Попробуйте ещё раз." + +#: lib/who_need_help_web/live/activity_live/show.ex:336 +#: lib/who_need_help_web/live/category_proposal_live.ex:60 +#: lib/who_need_help_web/live/request_live/show.ex:621 +#, elixir-autogen, elixir-format +msgid "Could not complete the action. Please try again." +msgstr "Не удалось выполнить действие. Попробуйте ещё раз." + +#: lib/who_need_help_web/live/activity_live/new.ex:105 +#, elixir-autogen, elixir-format +msgid "Could not publish the activity. Please try again." +msgstr "Не удалось опубликовать активность. Попробуйте ещё раз." + +#: lib/who_need_help_web/live/request_live/new.ex:144 +#, elixir-autogen, elixir-format +msgid "Could not publish the request. Please try again." +msgstr "Не удалось опубликовать заявку. Попробуйте ещё раз." + +#: lib/who_need_help_web/live/profile_live.ex:91 +#, elixir-autogen, elixir-format +msgid "Could not unblock this user. Please try again." +msgstr "Не удалось разблокировать пользователя. Попробуйте ещё раз." + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:22 +#, elixir-autogen, elixir-format +msgid "JavaScript is required to confirm this protected email link." +msgstr "Для подтверждения этой защищённой ссылки на email необходим JavaScript." + +#: lib/who_need_help_web/live/request_live/show.ex:594 +#, elixir-autogen, elixir-format +msgid "Last update: %{time}" +msgstr "Последнее обновление: %{time}" + +#: lib/who_need_help_web/live/request_live/show.ex:617 +#, elixir-autogen, elixir-format +msgid "Live location sharing is not active." +msgstr "Передача геопозиции сейчас не активна." + +#: lib/who_need_help_web/live/request_live/show.ex:592 +#, elixir-autogen, elixir-format +msgid "Shared location" +msgstr "Передаваемая геопозиция" + +#: lib/who_need_help_web/live/activity_live/show.ex:329 +#, elixir-autogen, elixir-format +msgid "The organizer cannot leave the activity." +msgstr "Организатор не может покинуть активность." + +#: lib/who_need_help_web/live/activity_live/show.ex:326 +#, elixir-autogen, elixir-format +msgid "The organizer is already attending." +msgstr "Организатор уже участвует." + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "The registration form is invalid." +msgstr "Форма регистрации заполнена неверно." + +#: lib/who_need_help_web/live/moderation_live.ex:239 +#, elixir-autogen, elixir-format +msgid "The selected record is no longer available." +msgstr "Выбранная запись больше недоступна." + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "The settings form is invalid." +msgstr "Форма настроек заполнена неверно." + +#: lib/who_need_help_web/controllers/user_session_controller.ex:111 +#, elixir-autogen, elixir-format +msgid "The sign-in form is invalid." +msgstr "Форма входа заполнена неверно." + +#: lib/who_need_help_web/live/activity_live/show.ex:323 +#, elixir-autogen, elixir-format +msgid "This activity is no longer available." +msgstr "Эта активность больше недоступна." + +#: lib/who_need_help_web/live/request_live/show.ex:616 +#, elixir-autogen, elixir-format +msgid "This match is no longer active." +msgstr "Это сопоставление больше не активно." + +#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#, elixir-autogen, elixir-format +msgid "This proposal is no longer available." +msgstr "Это предложение больше недоступно." + +#: lib/who_need_help_web/live/request_live/show.ex:614 +#, elixir-autogen, elixir-format +msgid "This request has expired." +msgstr "Срок действия этой заявки истёк." + +#: lib/who_need_help_web/controllers/user_session_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "Too many sign-in attempts in the configured time window." +msgstr "Слишком много попыток входа за настроенный промежуток времени." + +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:46 +#, elixir-autogen, elixir-format +msgid "Your secure email link is ready. Continue to sign in." +msgstr "Защищённая ссылка из письма готова. Продолжите вход." + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:128 +#, elixir-autogen, elixir-format +msgid "The confirmation email could not be sent. Please try again." +msgstr "Не удалось отправить письмо с подтверждением. Попробуйте ещё раз." + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:109 +#, elixir-autogen, elixir-format +msgid "If this address can be registered or signed in, login instructions will arrive shortly." +msgstr "Если этот адрес можно зарегистрировать или использовать для входа, инструкции для входа скоро придут." + +#: lib/who_need_help_web/live/profile_live.ex:62 +#, elixir-autogen, elixir-format +msgid "This account cannot perform that action." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:40 +#, elixir-autogen, elixir-format +msgid "Too many email-change requests in the configured time window." +msgstr "Слишком много запросов на смену email за настроенный промежуток времени." diff --git a/priv/gettext/ru/LC_MESSAGES/errors.po b/priv/gettext/ru/LC_MESSAGES/errors.po index 758ddef..ba70987 100644 --- a/priv/gettext/ru/LC_MESSAGES/errors.po +++ b/priv/gettext/ru/LC_MESSAGES/errors.po @@ -115,12 +115,12 @@ msgstr "должно быть равно %{number}" msgid "contains an invalid field definition" msgstr "содержит неверное определение поля" -#: lib/who_need_help_web/validation_messages.ex:22 +#: lib/who_need_help_web/validation_messages.ex:23 #, elixir-autogen, elixir-format msgid "did not change" msgstr "не изменилось" -#: lib/who_need_help_web/validation_messages.ex:23 +#: lib/who_need_help_web/validation_messages.ex:24 #, elixir-autogen, elixir-format msgid "does not match password" msgstr "не совпадает с паролем" @@ -130,16 +130,11 @@ msgstr "не совпадает с паролем" msgid "field keys must be unique" msgstr "ключи полей должны быть уникальными" -#: lib/who_need_help_web/validation_messages.ex:19 +#: lib/who_need_help_web/validation_messages.ex:20 #, elixir-autogen, elixir-format msgid "must be a GitHub profile URL" msgstr "должно быть URL профиля GitHub" -#: lib/who_need_help_web/validation_messages.ex:18 -#, elixir-autogen, elixir-format -msgid "must be a complete http(s) URL" -msgstr "должно быть полным URL с http(s)" - #: lib/who_need_help_web/validation_messages.ex:12 #, elixir-autogen, elixir-format msgid "must be an object with a fields array" @@ -165,7 +160,7 @@ msgstr "должно содержать хотя бы одно непустое msgid "must contain non-empty localized descriptions" msgstr "должно содержать непустые локализованные описания" -#: lib/who_need_help_web/validation_messages.ex:21 +#: lib/who_need_help_web/validation_messages.ex:22 #, elixir-autogen, elixir-format msgid "must have the @ sign and no spaces" msgstr "должно содержать знак @ и не содержать пробелов" @@ -185,7 +180,17 @@ msgstr "выберите категорию активности" msgid "select exactly one report target" msgstr "выберите ровно один объект жалобы" -#: lib/who_need_help_web/validation_messages.ex:20 +#: lib/who_need_help_web/validation_messages.ex:21 #, elixir-autogen, elixir-format msgid "you must confirm that you are 18+ and accept the rules" msgstr "подтвердите, что вам исполнилось 18 лет и вы принимаете правила" + +#: lib/who_need_help_web/validation_messages.ex:18 +#, elixir-autogen, elixir-format +msgid "must be a complete HTTPS URL" +msgstr "должно быть полным HTTPS URL" + +#: lib/who_need_help_web/validation_messages.ex:19 +#, elixir-autogen, elixir-format +msgid "must be a full HTTPS URL" +msgstr "должно быть полным HTTPS URL" diff --git a/priv/gettext/uk/LC_MESSAGES/default.po b/priv/gettext/uk/LC_MESSAGES/default.po index 86dcf3d..4a4d4e6 100644 --- a/priv/gettext/uk/LC_MESSAGES/default.po +++ b/priv/gettext/uk/LC_MESSAGES/default.po @@ -11,30 +11,32 @@ msgstr "" "Language: uk\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100 != 11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10||n%100>=20) ? 1 : 2);\n" -#: lib/who_need_help_web/components/layouts/root.html.heex:67 +#: lib/who_need_help_web/components/layouts.ex:111 +#: lib/who_need_help_web/components/layouts.ex:257 #, elixir-autogen, elixir-format msgid "Account settings" msgstr "Параметри облікового запису" -#: lib/who_need_help_web/components/core_components.ex:375 +#: lib/who_need_help_web/components/core_components.ex:393 #, elixir-autogen, elixir-format msgid "Actions" msgstr "Дії" -#: lib/who_need_help_web/components/layouts.ex:83 +#: lib/who_need_help_web/components/layouts.ex:70 +#: lib/who_need_help_web/components/layouts.ex:119 #: lib/who_need_help_web/controllers/page_html/home.html.heex:21 #: lib/who_need_help_web/live/request_live/new.ex:22 #, elixir-autogen, elixir-format msgid "Ask for help" msgstr "Попросити допомоги" -#: lib/who_need_help_web/components/layouts.ex:140 -#: lib/who_need_help_web/components/layouts.ex:155 +#: lib/who_need_help_web/components/layouts.ex:295 +#: lib/who_need_help_web/components/layouts.ex:310 #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "Спроба повторного з’єднання" -#: lib/who_need_help_web/components/layouts.ex:59 +#: lib/who_need_help_web/components/layouts.ex:171 #, elixir-autogen, elixir-format msgid "Categories" msgstr "Категорії" @@ -54,33 +56,34 @@ msgstr "Допомога може бути ближче, ніж здається msgid "Join the community" msgstr "Приєднуйтеся до спільноти" -#: lib/who_need_help_web/components/layouts.ex:89 -#: lib/who_need_help_web/components/layouts/root.html.heex:77 +#: lib/who_need_help_web/components/layouts.ex:78 +#: lib/who_need_help_web/components/layouts.ex:128 #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:9 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:45 #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:5 #, elixir-autogen, elixir-format msgid "Log in" msgstr "Вхід" -#: lib/who_need_help_web/components/layouts/root.html.heex:70 +#: lib/who_need_help_web/components/layouts.ex:114 +#: lib/who_need_help_web/components/layouts.ex:260 #, elixir-autogen, elixir-format msgid "Log out" msgstr "Вийти" -#: lib/who_need_help_web/components/layouts.ex:78 -#: lib/who_need_help_web/components/layouts/root.html.heex:64 +#: lib/who_need_help_web/components/layouts.ex:108 +#: lib/who_need_help_web/components/layouts.ex:254 #: lib/who_need_help_web/live/profile_live.ex:16 #, elixir-autogen, elixir-format msgid "Profile" msgstr "Профіль" -#: lib/who_need_help_web/components/layouts/root.html.heex:74 +#: lib/who_need_help_web/components/layouts.ex:75 +#: lib/who_need_help_web/components/layouts.ex:125 #, elixir-autogen, elixir-format msgid "Register" msgstr "Зареєструватися" -#: lib/who_need_help_web/components/layouts.ex:49 +#: lib/who_need_help_web/components/layouts.ex:161 #, elixir-autogen, elixir-format msgid "Requests" msgstr "Запити" @@ -90,12 +93,12 @@ msgstr "Запити" msgid "See nearby requests" msgstr "Переглянути заявки поруч" -#: lib/who_need_help_web/components/layouts.ex:147 +#: lib/who_need_help_web/components/layouts.ex:302 #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "Щось пішло не так!" -#: lib/who_need_help_web/components/layouts.ex:132 +#: lib/who_need_help_web/components/layouts.ex:287 #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "Немає з’єднання з інтернетом" @@ -105,67 +108,67 @@ msgstr "Немає з’єднання з інтернетом" msgid "close" msgstr "закрити" -#: lib/who_need_help_web/live/activity_live/show.ex:344 +#: lib/who_need_help_web/live/activity_live/show.ex:363 #, elixir-autogen, elixir-format msgid "%{approved}/%{capacity} approved" msgstr "Схвалено: %{approved}/%{capacity}" -#: lib/who_need_help_web/live/moderation_live.ex:481 +#: lib/who_need_help_web/live/moderation_live.ex:485 #, elixir-autogen, elixir-format msgid "%{count} community votes" msgstr "Голосів спільноти: %{count}" -#: lib/who_need_help_web/live/request_live/show.ex:637 +#: lib/who_need_help_web/live/request_live/show.ex:745 #, elixir-autogen, elixir-format msgid "%{count} unique people" msgstr "%{count} унікальних людей" -#: lib/who_need_help_web/live/request_live/show.ex:718 +#: lib/who_need_help_web/live/request_live/show.ex:829 #, elixir-autogen, elixir-format msgid "1 — unsafe" msgstr "1 — небезпечно" -#: lib/who_need_help_web/live/request_live/show.ex:717 +#: lib/who_need_help_web/live/request_live/show.ex:828 #, elixir-autogen, elixir-format msgid "2 — poor" msgstr "2 — погано" -#: lib/who_need_help_web/live/request_live/show.ex:716 +#: lib/who_need_help_web/live/request_live/show.ex:827 #, elixir-autogen, elixir-format msgid "3 — okay" msgstr "3 — нормально" -#: lib/who_need_help_web/live/request_live/show.ex:715 +#: lib/who_need_help_web/live/request_live/show.ex:826 #, elixir-autogen, elixir-format msgid "4 — good" msgstr "4 — добре" -#: lib/who_need_help_web/live/request_live/show.ex:714 +#: lib/who_need_help_web/live/request_live/show.ex:825 #, elixir-autogen, elixir-format msgid "5 — excellent" msgstr "5 — відмінно" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:31 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:118 #, elixir-autogen, elixir-format msgid "A link to confirm your email change has been sent to the new address." msgstr "" "Посилання для підтвердження зміни електронної пошти надіслано на нову " "адресу." -#: lib/who_need_help_web/live/moderation_live.ex:433 +#: lib/who_need_help_web/live/moderation_live.ex:437 #, elixir-autogen, elixir-format msgid "A signal requests human review; optional tracking absence is not proof of abuse." msgstr "" "Сигнал потребує перевірки людиною; відсутність необов’язкового відстеження " "не є доказом зловживання." -#: lib/who_need_help_web/live/request_live/show.ex:748 +#: lib/who_need_help_web/live/request_live/show.ex:859 #, elixir-autogen, elixir-format msgid "Accept only if you can complete this safely and for free." msgstr "" "Приймайте заявку, лише якщо можете безпечно й безкоштовно її виконати." -#: lib/who_need_help_web/live/request_live/show.ex:535 +#: lib/who_need_help_web/live/request_live/show.ex:633 #, elixir-autogen, elixir-format msgid "Accepted" msgstr "Прийнято" @@ -175,34 +178,30 @@ msgstr "Прийнято" msgid "Account Settings" msgstr "Параметри облікового запису" -#: lib/who_need_help_web/components/layouts/root.html.heex:54 +#: lib/who_need_help_web/components/layouts.ex:63 +#: lib/who_need_help_web/components/layouts.ex:192 #, elixir-autogen, elixir-format msgid "Account and language" msgstr "Акаунт і мова" -#: lib/who_need_help_web/live/moderation_live.ex:565 +#: lib/who_need_help_web/live/moderation_live.ex:569 #, elixir-autogen, elixir-format msgid "Accounts" msgstr "Акаунти" -#: lib/who_need_help_web/live/request_live/show.ex:744 +#: lib/who_need_help_web/live/request_live/show.ex:855 #, elixir-autogen, elixir-format msgid "Action center" msgstr "Центр дій" -#: lib/who_need_help_web/live/moderation_live.ex:261 -#: lib/who_need_help_web/live/moderation_live.ex:610 +#: lib/who_need_help_web/live/moderation_live.ex:265 +#: lib/who_need_help_web/live/moderation_live.ex:614 #, elixir-autogen, elixir-format msgid "Active" msgstr "Активний" -#: lib/who_need_help_web/live/request_live/show.ex:502 -#, elixir-autogen, elixir-format -msgid "Active match only" -msgstr "Лише для активної заявки" - -#: lib/who_need_help_web/components/layouts.ex:54 -#: lib/who_need_help_web/live/activity_live/index.ex:68 +#: lib/who_need_help_web/components/layouts.ex:166 +#: lib/who_need_help_web/live/activity_live/index.ex:118 #, elixir-autogen, elixir-format msgid "Activities" msgstr "Активності" @@ -218,7 +217,7 @@ msgstr "" "узгодьте маршрут і заходи безпеки та ніколи не публікуйте домашні адреси, " "коди доступу чи документи для подорожі." -#: lib/who_need_help_web/live/activity_live/new.ex:161 +#: lib/who_need_help_web/live/activity_live/new.ex:163 #, elixir-autogen, elixir-format msgid "Activity details" msgstr "Деталі активності" @@ -228,8 +227,8 @@ msgstr "Деталі активності" msgid "Activity hidden." msgstr "Активність приховано." -#: lib/who_need_help_web/live/activity_live/show.ex:16 -#: lib/who_need_help_web/live/activity_live/show.ex:182 +#: lib/who_need_help_web/live/activity_live/show.ex:18 +#: lib/who_need_help_web/live/activity_live/show.ex:177 #, elixir-autogen, elixir-format msgid "Activity is unavailable." msgstr "Активність недоступна." @@ -244,29 +243,29 @@ msgstr "Активність опубліковано. Ви схвалюєте msgid "Activity restored." msgstr "Активність відновлено." -#: lib/who_need_help_web/live/activity_live/index.ex:140 -#: lib/who_need_help_web/live/activity_live/new.ex:137 +#: lib/who_need_help_web/live/activity_live/index.ex:210 +#: lib/who_need_help_web/live/activity_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Activity type" msgstr "Тип активності" -#: lib/who_need_help_web/live/profile_live.ex:322 +#: lib/who_need_help_web/live/profile_live.ex:325 #, elixir-autogen, elixir-format msgid "Add unverified link" msgstr "Додати непідтверджене посилання" -#: lib/who_need_help_web/live/moderation_live.ex:268 -#: lib/who_need_help_web/live/moderation_live.ex:634 +#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:638 #, elixir-autogen, elixir-format msgid "Administrator" msgstr "Адміністратор" -#: lib/who_need_help_web/live/activity_live/index.ex:142 +#: lib/who_need_help_web/live/activity_live/index.ex:212 #, elixir-autogen, elixir-format msgid "All activities" msgstr "Усі активності" -#: lib/who_need_help_web/live/request_live/index.ex:152 +#: lib/who_need_help_web/live/request_live/index.ex:227 #, elixir-autogen, elixir-format msgid "All categories" msgstr "Усі категорії" @@ -276,72 +275,65 @@ msgstr "Усі категорії" msgid "Already registered?" msgstr "Уже зареєстровані?" -#: lib/who_need_help_web/controllers/user_registration_controller.ex:29 -#, elixir-autogen, elixir-format -msgid "An email was sent to %{email}, please access it to confirm your account." -msgstr "" -"Ми надіслали лист на %{email}. Відкрийте його, щоб підтвердити обліковий " -"запис." - -#: lib/who_need_help_web/live/request_live/show.ex:508 +#: lib/who_need_help_web/live/request_live/show.ex:603 #, elixir-autogen, elixir-format msgid "Another helper already accepted this request." msgstr "Інший помічник уже прийняв цю заявку." -#: lib/who_need_help_web/live/request_live/index.ex:167 +#: lib/who_need_help_web/live/request_live/index.ex:242 #, elixir-autogen, elixir-format msgid "Any urgency" msgstr "Будь-яка терміновість" -#: lib/who_need_help_web/live/activity_live/show.ex:505 +#: lib/who_need_help_web/live/activity_live/show.ex:524 #, elixir-autogen, elixir-format msgid "Approval pending" msgstr "Очікує схвалення" -#: lib/who_need_help_web/live/activity_live/show.ex:555 +#: lib/who_need_help_web/live/activity_live/show.ex:574 #, elixir-autogen, elixir-format msgid "Approve" msgstr "Схвалити" -#: lib/who_need_help_web/live/activity_live/show.ex:416 +#: lib/who_need_help_web/live/activity_live/show.ex:435 #, elixir-autogen, elixir-format msgid "Approved group chat" msgstr "Чат схваленої групи" -#: lib/who_need_help_web/live/activity_live/show.ex:581 +#: lib/who_need_help_web/live/activity_live/show.ex:600 #, elixir-autogen, elixir-format msgid "Approved participants" msgstr "Схвалені учасники" #: lib/who_need_help_web/controllers/page_html/home.html.heex:51 -#: lib/who_need_help_web/live/activity_live/new.ex:256 -#: lib/who_need_help_web/live/request_live/new.ex:304 +#: lib/who_need_help_web/live/activity_live/new.ex:258 +#: lib/who_need_help_web/live/request_live/new.ex:306 #, elixir-autogen, elixir-format msgid "Approximate area" msgstr "Орієнтовний район" -#: lib/who_need_help_web/live/activity_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Approximate public meeting area" msgstr "Орієнтовний публічний район зустрічі" -#: lib/who_need_help_web/live/profile_live.ex:164 -#: lib/who_need_help_web/live/request_live/show.ex:541 +#: lib/who_need_help_web/live/profile_live.ex:167 +#: lib/who_need_help_web/live/request_live/show.ex:639 #, elixir-autogen, elixir-format msgid "Approximate publicly" msgstr "Публічно — приблизно" -#: lib/who_need_help_web/live/request_live/new.ex:312 +#: lib/who_need_help_web/live/request_live/new.ex:314 #, elixir-autogen, elixir-format msgid "Approximate publicly (recommended)" msgstr "Показувати приблизно (рекомендовано)" -#: lib/who_need_help_web/live/request_live/show.ex:594 +#: lib/who_need_help_web/live/request_live/show.ex:702 #, elixir-autogen, elixir-format msgid "Area:" msgstr "Район:" -#: lib/who_need_help_web/live/request_live/new.ex:152 +#: lib/who_need_help_web/live/request_live/new.ex:154 #, elixir-autogen, elixir-format msgid "Ask for urgent help" msgstr "Попросити термінову допомогу" @@ -366,17 +358,17 @@ msgstr "" msgid "Block and report" msgstr "Блокування та скарга" -#: lib/who_need_help_web/live/activity_live/show.ex:622 +#: lib/who_need_help_web/live/activity_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Block organizer" msgstr "Заблокувати організатора" -#: lib/who_need_help_web/live/request_live/show.ex:949 +#: lib/who_need_help_web/live/request_live/show.ex:1062 #, elixir-autogen, elixir-format msgid "Block this user" msgstr "Заблокувати користувача" -#: lib/who_need_help_web/live/profile_live.ex:328 +#: lib/who_need_help_web/live/profile_live.ex:331 #, elixir-autogen, elixir-format msgid "Blocked users" msgstr "Заблоковані користувачі" @@ -388,12 +380,12 @@ msgstr "" "Обидва учасники підтверджують завершення, а помічник вводить одноразовий " "код." -#: lib/who_need_help_web/live/request_live/new.ex:115 +#: lib/who_need_help_web/live/request_live/new.ex:118 #, elixir-autogen, elixir-format msgid "Briefly describe the help you need" msgstr "Коротко опишіть, яка допомога вам потрібна" -#: lib/who_need_help_web/components/layouts.ex:106 +#: lib/who_need_help_web/components/layouts.ex:147 #, elixir-autogen, elixir-format msgid "Build Week feedback" msgstr "Звіт для Build Week" @@ -413,28 +405,28 @@ msgstr "ДОВІРА СПІЛЬНОТИ" msgid "COMMUNITY-DRIVEN TAXONOMY" msgstr "КАТЕГОРІЇ ВІД СПІЛЬНОТИ" -#: lib/who_need_help_web/live/activity_live/show.ex:575 +#: lib/who_need_help_web/live/activity_live/show.ex:594 #, elixir-autogen, elixir-format msgid "Cancel activity" msgstr "Скасувати активність" -#: lib/who_need_help_web/live/request_live/show.ex:762 -#: lib/who_need_help_web/live/request_live/show.ex:856 +#: lib/who_need_help_web/live/request_live/show.ex:873 +#: lib/who_need_help_web/live/request_live/show.ex:967 #, elixir-autogen, elixir-format msgid "Cancel request" msgstr "Скасувати запит" -#: lib/who_need_help_web/live/activity_live/index.ex:99 -#: lib/who_need_help_web/live/activity_live/show.ex:321 -#: lib/who_need_help_web/live/request_live/index.ex:110 -#: lib/who_need_help_web/live/request_live/index.ex:111 -#: lib/who_need_help_web/live/request_live/show.ex:538 +#: lib/who_need_help_web/live/activity_live/index.ex:168 +#: lib/who_need_help_web/live/activity_live/show.ex:340 +#: lib/who_need_help_web/live/request_live/index.ex:184 +#: lib/who_need_help_web/live/request_live/index.ex:185 +#: lib/who_need_help_web/live/request_live/show.ex:636 #, elixir-autogen, elixir-format msgid "Cancelled" msgstr "Скасовано" -#: lib/who_need_help_web/live/request_live/index.ex:150 -#: lib/who_need_help_web/live/request_live/new.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:225 +#: lib/who_need_help_web/live/request_live/new.ex:180 #, elixir-autogen, elixir-format msgid "Category" msgstr "Категорія" @@ -444,14 +436,14 @@ msgstr "Категорія" msgid "Category created and proposal approved." msgstr "Категорію створено, а пропозицію схвалено." -#: lib/who_need_help_web/live/request_live/new.ex:209 -#: lib/who_need_help_web/live/request_live/show.ex:583 +#: lib/who_need_help_web/live/request_live/new.ex:211 +#: lib/who_need_help_web/live/request_live/show.ex:691 #, elixir-autogen, elixir-format msgid "Category details" msgstr "Деталі категорії" #: lib/who_need_help_web/live/category_proposal_live.ex:66 -#: lib/who_need_help_web/live/moderation_live.ex:462 +#: lib/who_need_help_web/live/moderation_live.ex:466 #, elixir-autogen, elixir-format msgid "Category proposals" msgstr "Пропозиції категорій" @@ -461,7 +453,7 @@ msgstr "Пропозиції категорій" msgid "Change Email" msgstr "Змінити електронну пошту" -#: lib/who_need_help_web/live/moderation_live.ex:638 +#: lib/who_need_help_web/live/moderation_live.ex:642 #, elixir-autogen, elixir-format msgid "Change role" msgstr "Змінити роль" @@ -478,53 +470,53 @@ msgid "Choose a category, urgency, and safe location visibility." msgstr "" "Оберіть категорію, терміновість і безпечний рівень видимості геопозиції." -#: lib/who_need_help_web/live/activity_live/new.ex:138 +#: lib/who_need_help_web/live/activity_live/new.ex:140 #, elixir-autogen, elixir-format msgid "Choose activity" msgstr "Оберіть активність" -#: lib/who_need_help_web/live/request_live/new.ex:179 +#: lib/who_need_help_web/live/request_live/new.ex:181 #, elixir-autogen, elixir-format msgid "Choose category" msgstr "Виберіть категорію" -#: lib/who_need_help_web/live/activity_live/new.ex:173 -#: lib/who_need_help_web/live/activity_live/new.ex:200 -#: lib/who_need_help_web/live/request_live/new.ex:221 -#: lib/who_need_help_web/live/request_live/new.ex:248 +#: lib/who_need_help_web/live/activity_live/new.ex:175 +#: lib/who_need_help_web/live/activity_live/new.ex:202 +#: lib/who_need_help_web/live/request_live/new.ex:223 +#: lib/who_need_help_web/live/request_live/new.ex:250 #, elixir-autogen, elixir-format msgid "Choose…" msgstr "Оберіть…" -#: lib/who_need_help_web/live/activity_live/new.ex:249 +#: lib/who_need_help_web/live/activity_live/new.ex:251 #, elixir-autogen, elixir-format msgid "City centre, near the main entrance" msgstr "Центр міста, біля головного входу" -#: lib/who_need_help_web/live/activity_live/show.ex:635 -#: lib/who_need_help_web/live/request_live/show.ex:965 +#: lib/who_need_help_web/live/activity_live/show.ex:654 +#: lib/who_need_help_web/live/request_live/show.ex:1078 #, elixir-autogen, elixir-format msgid "Clear" msgstr "Очистити" -#: lib/who_need_help_web/live/moderation_live.ex:387 +#: lib/who_need_help_web/live/moderation_live.ex:391 #, elixir-autogen, elixir-format msgid "Close" msgstr "Закрити" -#: lib/who_need_help_web/live/activity_live/new.ex:218 +#: lib/who_need_help_web/live/activity_live/new.ex:220 #, elixir-autogen, elixir-format msgid "Coffee and conversation" msgstr "Кава й розмова" -#: lib/who_need_help_web/live/activity_live/index.ex:113 +#: lib/who_need_help_web/live/activity_live/index.ex:182 #, elixir-autogen, elixir-format msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgstr "" "Кава, кіно, прогулянки та походи відокремлені від термінової допомоги й " "ніколи не впливають на рейтинг помічника." -#: lib/who_need_help_web/live/request_live/show.ex:724 +#: lib/who_need_help_web/live/request_live/show.ex:835 #, elixir-autogen, elixir-format msgid "Comment (optional)" msgstr "Коментар (необов’язково)" @@ -534,35 +526,35 @@ msgstr "Коментар (необов’язково)" msgid "Community helpers" msgstr "Помічники спільноти" -#: lib/who_need_help_web/live/activity_live/index.ex:191 -#: lib/who_need_help_web/live/activity_live/show.ex:373 -#: lib/who_need_help_web/live/activity_live/show.ex:527 -#: lib/who_need_help_web/live/activity_live/show.ex:584 -#: lib/who_need_help_web/live/request_live/index.ex:211 +#: lib/who_need_help_web/live/activity_live/index.ex:262 +#: lib/who_need_help_web/live/activity_live/show.ex:392 +#: lib/who_need_help_web/live/activity_live/show.ex:546 +#: lib/who_need_help_web/live/activity_live/show.ex:603 +#: lib/who_need_help_web/live/request_live/index.ex:287 #, elixir-autogen, elixir-format msgid "Community member" msgstr "Член спільноти" -#: lib/who_need_help_web/live/activity_live/index.ex:98 -#: lib/who_need_help_web/live/activity_live/show.ex:320 -#: lib/who_need_help_web/live/request_live/index.ex:108 -#: lib/who_need_help_web/live/request_live/index.ex:109 -#: lib/who_need_help_web/live/request_live/show.ex:537 +#: lib/who_need_help_web/live/activity_live/index.ex:167 +#: lib/who_need_help_web/live/activity_live/show.ex:339 +#: lib/who_need_help_web/live/request_live/index.ex:182 +#: lib/who_need_help_web/live/request_live/index.ex:183 +#: lib/who_need_help_web/live/request_live/show.ex:635 #, elixir-autogen, elixir-format msgid "Completed" msgstr "Завершено" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:27 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:61 #, elixir-autogen, elixir-format msgid "Confirm and log in only this time" msgstr "Підтвердити й увійти лише зараз" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:21 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:55 #, elixir-autogen, elixir-format msgid "Confirm and stay logged in" msgstr "Підтвердити й залишатися в системі" -#: lib/who_need_help_web/live/request_live/show.ex:836 +#: lib/who_need_help_web/live/request_live/show.ex:947 #, elixir-autogen, elixir-format msgid "Confirm my part is complete" msgstr "Підтвердити виконання своєї частини" @@ -573,14 +565,14 @@ msgid "Confirm new password" msgstr "Підтвердити новий пароль" #: lib/who_need_help_web/live/category_proposal_live.ex:51 -#: lib/who_need_help_web/live/request_live/show.ex:516 +#: lib/who_need_help_web/live/request_live/show.ex:611 #, elixir-autogen, elixir-format msgid "Confirm your account and ensure it is active first." msgstr "" "Спочатку підтвердьте обліковий запис і переконайтеся, що він активний." -#: lib/who_need_help_web/live/activity_live/new.ex:97 -#: lib/who_need_help_web/live/request_live/new.ex:136 +#: lib/who_need_help_web/live/activity_live/new.ex:100 +#: lib/who_need_help_web/live/request_live/new.ex:139 #, elixir-autogen, elixir-format msgid "Confirm your email and ensure your account is active before publishing." msgstr "" @@ -592,128 +584,102 @@ msgstr "" msgid "Confirmation instructions" msgstr "Інструкції для підтвердження" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:18 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:24 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:52 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:58 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:15 #, elixir-autogen, elixir-format msgid "Confirming..." msgstr "Підтвердження…" -#: lib/who_need_help_web/live/moderation_live.ex:238 -#, elixir-autogen, elixir-format -msgid "Could not complete moderation: %{reason}" -msgstr "Не вдалося завершити модерацію: %{reason}" - -#: lib/who_need_help_web/live/activity_live/show.ex:317 -#: lib/who_need_help_web/live/category_proposal_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:523 -#, elixir-autogen, elixir-format -msgid "Could not complete the action: %{reason}" -msgstr "Не вдалося завершити дію: %{reason}" - -#: lib/who_need_help_web/live/activity_live/new.ex:103 -#, elixir-autogen, elixir-format -msgid "Could not publish the activity: %{reason}" -msgstr "Не вдалося опублікувати активність: %{reason}" - -#: lib/who_need_help_web/live/request_live/new.ex:142 -#, elixir-autogen, elixir-format -msgid "Could not publish the request: %{reason}" -msgstr "Не вдалося опублікувати заявку: %{reason}" - -#: lib/who_need_help_web/live/profile_live.ex:87 -#, elixir-autogen, elixir-format -msgid "Could not unblock: %{reason}" -msgstr "Не вдалося розблокувати: %{reason}" - -#: lib/who_need_help_web/live/activity_live/index.ex:159 +#: lib/who_need_help_web/live/activity_live/index.ex:229 #, elixir-autogen, elixir-format msgid "Create a clear plan in a public place." msgstr "Створіть зрозумілий план у громадському місці." -#: lib/who_need_help_web/live/activity_live/index.ex:119 +#: lib/who_need_help_web/live/activity_live/index.ex:188 #: lib/who_need_help_web/live/activity_live/new.ex:11 #, elixir-autogen, elixir-format msgid "Create activity" msgstr "Створити активність" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:60 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 #, elixir-autogen, elixir-format msgid "Create an account" msgstr "Створити обліковий запис" -#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/new.ex:115 #, elixir-autogen, elixir-format msgid "Create an activity" msgstr "Створити активність" -#: lib/who_need_help_web/live/moderation_live.ex:521 +#: lib/who_need_help_web/live/moderation_live.ex:525 #, elixir-autogen, elixir-format msgid "Create category" msgstr "Створити категорію" -#: lib/who_need_help_web/live/request_live/index.ex:129 +#: lib/who_need_help_web/live/request_live/index.ex:203 #, elixir-autogen, elixir-format msgid "Create urgent request" msgstr "Створити термінову заявку" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:59 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:58 #, elixir-autogen, elixir-format msgid "Creating account..." msgstr "Створення облікового запису…" -#: lib/who_need_help_web/live/activity_live/show.ex:644 +#: lib/who_need_help_web/live/activity_live/show.ex:663 #, elixir-autogen, elixir-format msgid "Dangerous plan" msgstr "Небезпечний план" -#: lib/who_need_help_web/live/moderation_live.ex:244 -#: lib/who_need_help_web/live/request_live/show.ex:973 +#: lib/who_need_help_web/live/moderation_live.ex:248 +#: lib/who_need_help_web/live/request_live/show.ex:1086 #, elixir-autogen, elixir-format msgid "Dangerous request" msgstr "Небезпечна заявка" -#: lib/who_need_help_web/live/moderation_live.ex:573 +#: lib/who_need_help_web/live/moderation_live.ex:577 #, elixir-autogen, elixir-format msgid "Decision" msgstr "Рішення" -#: lib/who_need_help_web/live/moderation_live.ex:519 +#: lib/who_need_help_web/live/moderation_live.ex:523 #, elixir-autogen, elixir-format msgid "Decision note" msgstr "Примітка до рішення" -#: lib/who_need_help_web/live/moderation_live.ex:289 +#: lib/who_need_help_web/live/moderation_live.ex:293 #, elixir-autogen, elixir-format msgid "Decisions and access to reported chat evidence are written to the audit log." msgstr "" "Рішення та доступ до матеріалів зі скарг на листування записуються до " "журналу аудиту." -#: lib/who_need_help_web/live/activity_live/show.ex:562 +#: lib/who_need_help_web/live/activity_live/show.ex:581 #, elixir-autogen, elixir-format msgid "Decline" msgstr "Відхилити" -#: lib/who_need_help_web/live/profile_live.ex:161 +#: lib/who_need_help_web/live/profile_live.ex:164 #, elixir-autogen, elixir-format msgid "Default location visibility" msgstr "Типова видимість геопозиції" -#: lib/who_need_help_web/live/activity_live/new.ex:225 +#: lib/who_need_help_web/live/activity_live/new.ex:227 #, elixir-autogen, elixir-format msgid "Describe the public plan, approximate duration, and who might enjoy it." msgstr "" "Опишіть публічний план, приблизну тривалість і кому він може бути цікавим." -#: lib/who_need_help_web/live/moderation_live.ex:256 -#: lib/who_need_help_web/live/moderation_live.ex:362 -#: lib/who_need_help_web/live/moderation_live.ex:448 +#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:366 +#: lib/who_need_help_web/live/moderation_live.ex:452 #, elixir-autogen, elixir-format msgid "Dismissed" msgstr "Відхилено" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:35 -#: lib/who_need_help_web/live/profile_live.ex:146 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34 +#: lib/who_need_help_web/live/profile_live.ex:149 #, elixir-autogen, elixir-format msgid "Display name" msgstr "Відображуване ім’я" @@ -731,25 +697,26 @@ msgstr "" msgid "Don't have an account?" msgstr "Немає акаунта?" -#: lib/who_need_help_web/live/request_live/show.ex:704 +#: lib/who_need_help_web/live/request_live/show.ex:815 #, elixir-autogen, elixir-format msgid "Double-blind review" msgstr "Двосторонній прихований відгук" #: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:25 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:38 -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:56 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:71 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89 #: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15 #, elixir-autogen, elixir-format msgid "Email" msgstr "Електронна пошта" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:65 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:89 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:96 #, elixir-autogen, elixir-format msgid "Email change link is invalid or it has expired." msgstr "Посилання для зміни електронної пошти недійсне або прострочене." -#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:84 #, elixir-autogen, elixir-format msgid "Email changed successfully." msgstr "Адресу електронної пошти успішно змінено." @@ -763,22 +730,22 @@ msgstr "" "Перевіряйте історію людини, унікальні взаємодії, підтвердження передачі та " "відкриті відгуки й зустрічайтеся лише там, де почуваєтеся безпечно." -#: lib/who_need_help_web/live/profile_live.ex:153 +#: lib/who_need_help_web/live/profile_live.ex:156 #, elixir-autogen, elixir-format msgid "English" msgstr "Англійська" -#: lib/who_need_help_web/live/moderation_live.ex:502 +#: lib/who_need_help_web/live/moderation_live.ex:506 #, elixir-autogen, elixir-format msgid "English description" msgstr "Опис англійською" -#: lib/who_need_help_web/live/moderation_live.ex:491 +#: lib/who_need_help_web/live/moderation_live.ex:495 #, elixir-autogen, elixir-format msgid "English name" msgstr "Назва англійською" -#: lib/who_need_help_web/live/request_live/show.ex:881 +#: lib/who_need_help_web/live/request_live/show.ex:992 #, elixir-autogen, elixir-format msgid "Enter handover code" msgstr "Введіть код передачі" @@ -788,51 +755,51 @@ msgstr "Введіть код передачі" msgid "Exact address after matching" msgstr "Точна адреса після призначення помічника" -#: lib/who_need_help_web/live/activity_live/new.ex:270 +#: lib/who_need_help_web/live/activity_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Exact coordinates are shown only to participants you approve." msgstr "Точні координати показуються лише схваленим вами учасникам." -#: lib/who_need_help_web/live/profile_live.ex:165 +#: lib/who_need_help_web/live/profile_live.ex:168 #, elixir-autogen, elixir-format msgid "Exact for active match" msgstr "Точно — для активної заявки" -#: lib/who_need_help_web/live/request_live/new.ex:314 -#: lib/who_need_help_web/live/request_live/show.ex:543 +#: lib/who_need_help_web/live/request_live/new.ex:316 +#: lib/who_need_help_web/live/request_live/show.ex:641 #, elixir-autogen, elixir-format msgid "Exact only for active match" msgstr "Точно лише для активної заявки" -#: lib/who_need_help_web/live/profile_live.ex:166 -#: lib/who_need_help_web/live/request_live/show.ex:544 +#: lib/who_need_help_web/live/profile_live.ex:169 +#: lib/who_need_help_web/live/request_live/show.ex:642 #, elixir-autogen, elixir-format msgid "Exact publicly" msgstr "Публічно — точно" -#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/new.ex:317 #, elixir-autogen, elixir-format msgid "Exact publicly — explicit consent" msgstr "Публічно — точно, з явною згодою" -#: lib/who_need_help_web/live/request_live/show.ex:602 +#: lib/who_need_help_web/live/request_live/show.ex:710 #, elixir-autogen, elixir-format msgid "Expires:" msgstr "Спливає:" -#: lib/who_need_help_web/live/request_live/new.ex:272 +#: lib/who_need_help_web/live/request_live/new.ex:274 #, elixir-autogen, elixir-format msgid "Explain what is already arranged and what the volunteer needs to do." msgstr "Поясніть, що вже організовано й що має зробити волонтер." -#: lib/who_need_help_web/live/activity_live/index.ex:110 +#: lib/who_need_help_web/live/activity_live/index.ex:179 #, elixir-autogen, elixir-format msgid "Find people to join" msgstr "Знайти компанію" -#: lib/who_need_help_web/live/activity_live/show.ex:645 -#: lib/who_need_help_web/live/moderation_live.ex:246 -#: lib/who_need_help_web/live/request_live/show.ex:975 +#: lib/who_need_help_web/live/activity_live/show.ex:664 +#: lib/who_need_help_web/live/moderation_live.ex:250 +#: lib/who_need_help_web/live/request_live/show.ex:1088 #, elixir-autogen, elixir-format msgid "Fraud" msgstr "Шахрайство" @@ -842,41 +809,41 @@ msgstr "Шахрайство" msgid "Free help and thanks" msgstr "Безкоштовна допомога й подяка" -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:51 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:62 #, elixir-autogen, elixir-format msgid "GitHub profile verified." msgstr "Профіль GitHub підтверджено." -#: lib/who_need_help_web/live/profile_live.ex:250 +#: lib/who_need_help_web/live/profile_live.ex:253 #, elixir-autogen, elixir-format msgid "GitHub verification is unavailable until the operator configures its OAuth credentials. No manually entered link is treated as verified." msgstr "" "Підтвердження через GitHub недоступне, доки оператор не налаштує облікові " "дані OAuth. Посилання, введене вручну, не вважається підтвердженим." -#: lib/who_need_help_web/live/request_live/show.ex:864 +#: lib/who_need_help_web/live/request_live/show.ex:975 #, elixir-autogen, elixir-format msgid "HANDOVER CODE FOR THE HELPER" msgstr "КОД ПЕРЕДАЧІ ДЛЯ ПОМІЧНИКА" -#: lib/who_need_help_web/live/profile_live.ex:319 +#: lib/who_need_help_web/live/profile_live.ex:322 #, elixir-autogen, elixir-format msgid "Handle (optional)" msgstr "Ім’я користувача (необов’язково)" -#: lib/who_need_help_web/live/request_live/show.ex:122 +#: lib/who_need_help_web/live/request_live/show.ex:147 #, elixir-autogen, elixir-format msgid "Handover code verified." msgstr "Код передачі підтверджено." -#: lib/who_need_help_web/live/activity_live/show.ex:643 -#: lib/who_need_help_web/live/moderation_live.ex:245 -#: lib/who_need_help_web/live/request_live/show.ex:974 +#: lib/who_need_help_web/live/activity_live/show.ex:662 +#: lib/who_need_help_web/live/moderation_live.ex:249 +#: lib/who_need_help_web/live/request_live/show.ex:1087 #, elixir-autogen, elixir-format msgid "Harassment" msgstr "Домагання" -#: lib/who_need_help_web/live/request_live/show.ex:110 +#: lib/who_need_help_web/live/request_live/show.ex:135 #, elixir-autogen, elixir-format msgid "Help is in progress." msgstr "Допомога виконується." @@ -895,12 +862,12 @@ msgstr "" msgid "Helper" msgstr "Помічник" -#: lib/who_need_help_web/live/request_live/show.ex:767 +#: lib/who_need_help_web/live/request_live/show.ex:878 #, elixir-autogen, elixir-format msgid "Helper: %{name}" msgstr "Помічник: %{name}" -#: lib/who_need_help_web/components/layouts.ex:64 +#: lib/who_need_help_web/components/layouts.ex:176 #, elixir-autogen, elixir-format msgid "Helpers" msgstr "Помічники" @@ -946,26 +913,26 @@ msgstr "" "\n" "Якщо ви не запитували цей лист, просто проігноруйте його." -#: lib/who_need_help_web/live/activity_live/new.ex:257 -#: lib/who_need_help_web/live/profile_live.ex:163 -#: lib/who_need_help_web/live/request_live/new.ex:313 -#: lib/who_need_help_web/live/request_live/show.ex:542 +#: lib/who_need_help_web/live/activity_live/new.ex:259 +#: lib/who_need_help_web/live/profile_live.ex:166 +#: lib/who_need_help_web/live/request_live/new.ex:315 +#: lib/who_need_help_web/live/request_live/show.ex:640 #, elixir-autogen, elixir-format msgid "Hidden" msgstr "Приховано" -#: lib/who_need_help_web/live/moderation_live.ex:346 +#: lib/who_need_help_web/live/moderation_live.ex:350 #, elixir-autogen, elixir-format msgid "Hide activity" msgstr "Приховати активність" -#: lib/who_need_help_web/live/moderation_live.ex:332 -#: lib/who_need_help_web/live/moderation_live.ex:344 +#: lib/who_need_help_web/live/moderation_live.ex:336 +#: lib/who_need_help_web/live/moderation_live.ex:348 #, elixir-autogen, elixir-format msgid "Hide reason" msgstr "Причина приховування" -#: lib/who_need_help_web/live/moderation_live.ex:334 +#: lib/who_need_help_web/live/moderation_live.ex:338 #, elixir-autogen, elixir-format msgid "Hide request" msgstr "Сховати запит" @@ -975,18 +942,18 @@ msgstr "Сховати запит" msgid "Human decisions retained" msgstr "Рішення залишаються за людиною" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48 #, elixir-autogen, elixir-format msgid "I am 18 or older and accept the safety rules" msgstr "Мені 18 років, і я приймаю правила безпеки." #: lib/who_need_help_web/controllers/page_html/home.html.heex:56 -#: lib/who_need_help_web/live/request_live/show.ex:751 +#: lib/who_need_help_web/live/request_live/show.ex:862 #, elixir-autogen, elixir-format msgid "I can help" msgstr "Я можу допомогти" -#: lib/who_need_help_web/live/request_live/new.ex:124 +#: lib/who_need_help_web/live/request_live/new.ex:127 #, elixir-autogen, elixir-format msgid "I confirm there is no immediate danger, people and vehicles are away from active traffic or emergency services have already been contacted, and the requested task is lawful." msgstr "" @@ -994,21 +961,21 @@ msgstr "" "перебувають поза активним рухом або екстрені служби вже викликано, а " "запитувана допомога є законною." -#: lib/who_need_help_web/live/request_live/new.ex:118 +#: lib/who_need_help_web/live/request_live/new.ex:121 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, medical advice request, controlled substance, or request to buy medicine on my behalf." msgstr "" "Я підтверджую, що це не екстрена ситуація, не запит медичної консультації чи" " контрольованої речовини й не прохання придбати ліки від мого імені." -#: lib/who_need_help_web/live/request_live/new.ex:130 +#: lib/who_need_help_web/live/request_live/new.ex:133 #, elixir-autogen, elixir-format msgid "I confirm this is not an emergency, there is no immediate danger, and the requested task is lawful." msgstr "" "Я підтверджую, що це не екстрена ситуація, безпосередньої небезпеки немає, а" " запитувана допомога є законною." -#: lib/who_need_help_web/live/activity_live/new.ex:292 +#: lib/who_need_help_web/live/activity_live/new.ex:298 #, elixir-autogen, elixir-format msgid "I will approve participants individually, keep sensitive details in the approved group, and use an appropriate public first meeting point." msgstr "" @@ -1020,21 +987,21 @@ msgstr "" msgid "Identity and reputation" msgstr "Особа й репутація" -#: lib/who_need_help_web/controllers/user_session_controller.ex:66 +#: lib/who_need_help_web/controllers/user_session_controller.ex:89 #, elixir-autogen, elixir-format msgid "If your email is in our system, you will receive instructions for logging in shortly." msgstr "" "Якщо ця адреса електронної пошти є в нашій системі, незабаром ви отримаєте " "інструкції для входу." -#: lib/who_need_help_web/live/activity_live/show.ex:647 -#: lib/who_need_help_web/live/moderation_live.ex:249 -#: lib/who_need_help_web/live/request_live/show.ex:978 +#: lib/who_need_help_web/live/activity_live/show.ex:666 +#: lib/who_need_help_web/live/moderation_live.ex:253 +#: lib/who_need_help_web/live/request_live/show.ex:1091 #, elixir-autogen, elixir-format msgid "Impersonation" msgstr "Видавання себе за іншу особу" -#: lib/who_need_help_web/live/request_live/show.ex:916 +#: lib/who_need_help_web/live/request_live/show.ex:1027 #, elixir-autogen, elixir-format msgid "In a browser this works only while the page remains open. The Android app shows a persistent notification and can continue after it is minimized. Raw coordinates are removed when sharing stops." msgstr "" @@ -1042,78 +1009,73 @@ msgstr "" "Android показує постійне сповіщення й може продовжувати передавання після " "згортання. Після зупинення передавання вихідні координати видаляються." -#: lib/who_need_help_web/live/request_live/index.ex:106 -#: lib/who_need_help_web/live/request_live/index.ex:107 -#: lib/who_need_help_web/live/request_live/show.ex:536 +#: lib/who_need_help_web/live/request_live/index.ex:180 +#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/show.ex:634 #, elixir-autogen, elixir-format msgid "In progress" msgstr "В процесі" -#: lib/who_need_help_web/live/profile_live.ex:151 +#: lib/who_need_help_web/live/profile_live.ex:154 #, elixir-autogen, elixir-format msgid "Interface language" msgstr "Мова інтерфейсу" -#: lib/who_need_help_web/live/moderation_live.ex:617 +#: lib/who_need_help_web/live/moderation_live.ex:621 #, elixir-autogen, elixir-format msgid "Internal note" msgstr "Внутрішня нотатка" -#: lib/who_need_help_web/controllers/user_session_controller.ex:47 +#: lib/who_need_help_web/controllers/user_session_controller.ex:124 #, elixir-autogen, elixir-format msgid "Invalid email or password" msgstr "Неправильна електронна пошта або пароль" -#: lib/who_need_help_web/live/activity_live/show.ex:301 +#: lib/who_need_help_web/live/activity_live/show.ex:314 #, elixir-autogen, elixir-format msgid "Join requests are closed." msgstr "Подання заявок на участь закрито." -#: lib/who_need_help_web/live/activity_live/show.ex:361 +#: lib/who_need_help_web/live/activity_live/show.ex:380 #, elixir-autogen, elixir-format msgid "Join requests close" msgstr "Приймання заявок завершується" -#: lib/who_need_help_web/live/activity_live/new.ex:237 +#: lib/who_need_help_web/live/activity_live/new.ex:239 #, elixir-autogen, elixir-format msgid "Join requests close (UTC)" msgstr "Завершення прийому заявок (UTC)" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:54 -#, elixir-autogen, elixir-format -msgid "Keep me logged in on this device" -msgstr "Не виходити з облікового запису на цьому пристрої" - -#: lib/who_need_help_web/live/activity_live/new.ex:278 -#: lib/who_need_help_web/live/request_live/new.ex:336 +#: lib/who_need_help_web/live/activity_live/new.ex:282 +#: lib/who_need_help_web/live/request_live/new.ex:340 #, elixir-autogen, elixir-format msgid "Latitude" msgstr "Широта" -#: lib/who_need_help_web/live/activity_live/show.ex:515 +#: lib/who_need_help_web/live/activity_live/show.ex:534 #, elixir-autogen, elixir-format msgid "Leave activity" msgstr "Залишити активність" -#: lib/who_need_help_web/live/request_live/show.ex:914 +#: lib/who_need_help_web/live/request_live/show.ex:1025 #, elixir-autogen, elixir-format msgid "Live location" msgstr "Геопозиція в реальному часі" #: lib/who_need_help_web/controllers/page_html/safety.html.heex:51 -#: lib/who_need_help_web/live/activity_live/show.ex:484 +#: lib/who_need_help_web/live/activity_live/show.ex:503 #, elixir-autogen, elixir-format msgid "Location privacy" msgstr "Приватність геопозиції" -#: lib/who_need_help_web/live/request_live/show.ex:803 +#: lib/who_need_help_web/live/request_live/show.ex:914 #, elixir-autogen, elixir-format msgid "Location sharing is optional; missing evidence is not proof of abuse." msgstr "" "Передавання геопозиції необов’язкове; відсутність таких даних не є доказом " "зловживання." -#: lib/who_need_help_web/live/request_live/new.ex:310 +#: lib/who_need_help_web/live/request_live/new.ex:312 #, elixir-autogen, elixir-format msgid "Location visibility" msgstr "Видимість геопозиції" @@ -1123,12 +1085,12 @@ msgstr "Видимість геопозиції" msgid "Location-supported people" msgstr "Люди з підтвердженням геопозицією" -#: lib/who_need_help_web/live/request_live/show.ex:609 +#: lib/who_need_help_web/live/request_live/show.ex:717 #, elixir-autogen, elixir-format msgid "Location:" msgstr "Геопозиція:" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:69 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102 #, elixir-autogen, elixir-format msgid "Log in and stay logged in" msgstr "Увійти й залишатися в системі" @@ -1138,44 +1100,27 @@ msgstr "Увійти й залишатися в системі" msgid "Log in instructions" msgstr "Інструкції для входу" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:72 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105 #, elixir-autogen, elixir-format msgid "Log in only this time" msgstr "Увійти лише зараз" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:45 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78 #, elixir-autogen, elixir-format msgid "Log in with email" msgstr "Вхід за допомогою ел. пошти" -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:60 -#, elixir-autogen, elixir-format -msgid "Log me in only this time" -msgstr "Увійти лише зараз" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:102 +#: lib/who_need_help_web/controllers/user_session_controller.ex:117 #, elixir-autogen, elixir-format msgid "Logged out successfully." msgstr "Ви успішно вийшли." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:42 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:51 -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:57 -#, elixir-autogen, elixir-format -msgid "Logging in..." -msgstr "Вхід..." - -#: lib/who_need_help_web/live/activity_live/new.ex:285 -#: lib/who_need_help_web/live/request_live/new.ex:343 +#: lib/who_need_help_web/live/activity_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/new.ex:347 #, elixir-autogen, elixir-format msgid "Longitude" msgstr "Довгота" -#: lib/who_need_help_web/controllers/user_session_controller.ex:95 -#, elixir-autogen, elixir-format -msgid "Magic link is invalid or it has expired." -msgstr "Посилання для входу недійсне або прострочене." - #: lib/who_need_help_web/controllers/feedback_html/show.html.heex:12 #, elixir-autogen, elixir-format msgid "Main Codex session ID" @@ -1186,7 +1131,7 @@ msgstr "Ідентифікатор основного сеансу Codex" msgid "Manage your account email address and password settings" msgstr "Керуйте електронною поштою та паролем облікового запису" -#: lib/who_need_help_web/live/profile_live.ex:232 +#: lib/who_need_help_web/live/profile_live.ex:235 #, elixir-autogen, elixir-format msgid "Manually added links are public and marked unverified. A verified badge is only granted after completing a provider authorization flow." msgstr "" @@ -1194,7 +1139,7 @@ msgstr "" "Значок підтвердження з’являється лише після авторизації у відповідного " "провайдера." -#: lib/who_need_help_web/live/activity_live/show.ex:572 +#: lib/who_need_help_web/live/activity_live/show.ex:591 #, elixir-autogen, elixir-format msgid "Mark completed" msgstr "Позначити завершеною" @@ -1204,14 +1149,14 @@ msgstr "Позначити завершеною" msgid "Match and coordinate" msgstr "Знайдіть помічника й узгодьте деталі" -#: lib/who_need_help_web/live/request_live/index.ex:104 -#: lib/who_need_help_web/live/request_live/index.ex:105 -#: lib/who_need_help_web/live/request_live/show.ex:534 +#: lib/who_need_help_web/live/request_live/index.ex:178 +#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/show.ex:632 #, elixir-autogen, elixir-format msgid "Matched" msgstr "Помічника знайдено" -#: lib/who_need_help_web/live/request_live/new.ex:110 +#: lib/who_need_help_web/live/request_live/new.ex:113 #, elixir-autogen, elixir-format msgid "Medicine is ready at the pharmacy" msgstr "Ліки готові до отримання в аптеці" @@ -1226,7 +1171,7 @@ msgstr "Замовлення ліків готове до отримання в msgid "Medicine pickup" msgstr "Доставка ліків" -#: lib/who_need_help_web/live/request_live/new.ex:154 +#: lib/who_need_help_web/live/request_live/new.ex:156 #, elixir-autogen, elixir-format msgid "Medicine pickup remains the first priority. Roadside categories use the same moderated, extensible request flow with details specific to each type of help." msgstr "" @@ -1239,7 +1184,7 @@ msgstr "" msgid "Medicine requests" msgstr "Запити на ліки" -#: lib/who_need_help_web/live/activity_live/index.ex:126 +#: lib/who_need_help_web/live/activity_live/index.ex:195 #, elixir-autogen, elixir-format msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgstr "" @@ -1247,27 +1192,27 @@ msgstr "" "лише схваленій групі й ідіть або надсилайте скаргу, якщо ситуація здається " "небезпечною." -#: lib/who_need_help_web/live/activity_live/show.ex:367 +#: lib/who_need_help_web/live/activity_live/show.ex:386 #, elixir-autogen, elixir-format msgid "Meeting area" msgstr "Область зустрічі" -#: lib/who_need_help_web/live/moderation_live.ex:548 +#: lib/who_need_help_web/live/moderation_live.ex:552 #, elixir-autogen, elixir-format msgid "Merge" msgstr "Об’єднати" -#: lib/who_need_help_web/live/moderation_live.ex:542 +#: lib/who_need_help_web/live/moderation_live.ex:546 #, elixir-autogen, elixir-format msgid "Merge into" msgstr "Об’єднати з" -#: lib/who_need_help_web/live/moderation_live.ex:547 +#: lib/who_need_help_web/live/moderation_live.ex:551 #, elixir-autogen, elixir-format msgid "Merge note" msgstr "Примітка до об’єднання" -#: lib/who_need_help_web/live/activity_live/show.ex:464 +#: lib/who_need_help_web/live/activity_live/show.ex:483 #, elixir-autogen, elixir-format msgid "Message the approved group" msgstr "Надіслати повідомлення схваленій групі" @@ -1277,41 +1222,41 @@ msgstr "Надіслати повідомлення схваленій груп msgid "Mode" msgstr "Режим" -#: lib/who_need_help_web/components/layouts.ex:73 +#: lib/who_need_help_web/components/layouts.ex:181 #: lib/who_need_help_web/live/moderation_live.ex:9 -#: lib/who_need_help_web/live/moderation_live.ex:287 +#: lib/who_need_help_web/live/moderation_live.ex:291 #, elixir-autogen, elixir-format msgid "Moderation" msgstr "Модерація" -#: lib/who_need_help_web/live/moderation_live.ex:267 -#: lib/who_need_help_web/live/moderation_live.ex:633 +#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:637 #, elixir-autogen, elixir-format msgid "Moderator" msgstr "Модератор" -#: lib/who_need_help_web/live/moderation_live.ex:226 -#: lib/who_need_help_web/user_auth.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/user_auth.ex:306 #, elixir-autogen, elixir-format msgid "Moderator access is required." msgstr "Потрібен доступ модератора." -#: lib/who_need_help_web/live/request_live/index.ex:122 +#: lib/who_need_help_web/live/request_live/index.ex:196 #, elixir-autogen, elixir-format msgid "NEARBY MUTUAL AID" msgstr "ВЗАЄМОДОПОМОГА ПОРУЧ" -#: lib/who_need_help_web/live/request_live/index.ex:64 +#: lib/who_need_help_web/live/request_live/index.ex:97 #, elixir-autogen, elixir-format msgid "Nearby help" msgstr "Допомога поруч" -#: lib/who_need_help_web/live/request_live/new.ex:113 +#: lib/who_need_help_web/live/request_live/new.ex:116 #, elixir-autogen, elixir-format msgid "Need roadside help in a safe location" msgstr "Потрібна допомога на узбіччі дороги" -#: lib/who_need_help_web/live/profile_live.ex:301 +#: lib/who_need_help_web/live/profile_live.ex:304 #, elixir-autogen, elixir-format msgid "Network" msgstr "Мережа" @@ -1321,10 +1266,10 @@ msgstr "Мережа" msgid "New password" msgstr "Новий пароль" -#: lib/who_need_help_web/live/activity_live/new.ex:205 -#: lib/who_need_help_web/live/activity_live/show.ex:283 -#: lib/who_need_help_web/live/request_live/new.ex:253 -#: lib/who_need_help_web/live/request_live/show.ex:472 +#: lib/who_need_help_web/live/activity_live/new.ex:207 +#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/request_live/new.ex:255 +#: lib/who_need_help_web/live/request_live/show.ex:563 #, elixir-autogen, elixir-format msgid "No" msgstr "Ні" @@ -1334,28 +1279,28 @@ msgstr "Ні" msgid "No completed help yet." msgstr "Завершеної допомоги поки немає." -#: lib/who_need_help_web/live/activity_live/show.ex:432 -#: lib/who_need_help_web/live/request_live/show.ex:663 +#: lib/who_need_help_web/live/activity_live/show.ex:451 +#: lib/who_need_help_web/live/request_live/show.ex:771 #, elixir-autogen, elixir-format msgid "No messages yet." msgstr "Ще немає повідомлень." -#: lib/who_need_help_web/live/activity_live/index.ex:157 +#: lib/who_need_help_web/live/activity_live/index.ex:227 #, elixir-autogen, elixir-format msgid "No open activities yet" msgstr "Відкритих активностей поки немає" -#: lib/who_need_help_web/live/request_live/index.ex:179 +#: lib/who_need_help_web/live/request_live/index.ex:254 #, elixir-autogen, elixir-format msgid "No open requests yet" msgstr "Відкритих заявок поки немає" -#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:460 #, elixir-autogen, elixir-format msgid "No open signals." msgstr "Немає відкритих сигналів." -#: lib/who_need_help_web/live/activity_live/show.ex:567 +#: lib/who_need_help_web/live/activity_live/show.ex:586 #, elixir-autogen, elixir-format msgid "No pending join requests." msgstr "Немає заявок на участь, що очікують." @@ -1365,17 +1310,17 @@ msgstr "Немає заявок на участь, що очікують." msgid "No proposals yet." msgstr "Пропозицій ще немає." -#: lib/who_need_help_web/live/moderation_live.ex:372 +#: lib/who_need_help_web/live/moderation_live.ex:376 #, elixir-autogen, elixir-format msgid "No reports." msgstr "Скарг немає." -#: lib/who_need_help_web/live/profile_live.ex:258 +#: lib/who_need_help_web/live/profile_live.ex:261 #, elixir-autogen, elixir-format msgid "No social links added." msgstr "Не додано соціальних посилань." -#: lib/who_need_help_web/live/profile_live.ex:209 +#: lib/who_need_help_web/live/profile_live.ex:212 #, elixir-autogen, elixir-format msgid "None revealed yet." msgstr "Ще нічого не відкрито." @@ -1392,22 +1337,22 @@ msgstr "" msgid "Not for emergencies" msgstr "Не для екстрених ситуацій" -#: lib/who_need_help_web/live/request_live/index.ex:94 -#: lib/who_need_help_web/live/request_live/index.ex:95 #: lib/who_need_help_web/live/request_live/index.ex:168 -#: lib/who_need_help_web/live/request_live/new.ex:291 -#: lib/who_need_help_web/live/request_live/show.ex:525 -#: lib/who_need_help_web/live/request_live/show.ex:526 +#: lib/who_need_help_web/live/request_live/index.ex:169 +#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/new.ex:293 +#: lib/who_need_help_web/live/request_live/show.ex:623 +#: lib/who_need_help_web/live/request_live/show.ex:624 #, elixir-autogen, elixir-format msgid "Now" msgstr "Зараз" -#: lib/who_need_help_web/live/activity_live/index.ex:108 +#: lib/who_need_help_web/live/activity_live/index.ex:177 #, elixir-autogen, elixir-format msgid "OPTIONAL SOCIAL PLANS" msgstr "НЕОБОВ’ЯЗКОВІ СПІЛЬНІ ПЛАНИ" -#: lib/who_need_help_web/live/request_live/index.ex:125 +#: lib/who_need_help_web/live/request_live/index.ex:199 #, elixir-autogen, elixir-format msgid "Only approximate areas are shown before a safe match." msgstr "" @@ -1422,29 +1367,29 @@ msgstr "" "видавання готівки, перепакування чи придбання від вашого імені. Дотримуйтеся" " правил аптеки й місцевого законодавства." -#: lib/who_need_help_web/live/moderation_live.ex:390 +#: lib/who_need_help_web/live/moderation_live.ex:394 #, elixir-autogen, elixir-format msgid "Only the conversation linked to this report is displayed." msgstr "Показано лише листування, пов’язане з цією скаргою." -#: lib/who_need_help_web/live/activity_live/show.ex:418 +#: lib/who_need_help_web/live/activity_live/show.ex:437 #, elixir-autogen, elixir-format msgid "Only the organizer and approved participants can read or send these messages." msgstr "" "Тільки організатор і схвалені учасники можуть читати або надсилати ці " "повідомлення." -#: lib/who_need_help_web/live/activity_live/index.ex:97 -#: lib/who_need_help_web/live/activity_live/show.ex:319 -#: lib/who_need_help_web/live/moderation_live.ex:253 -#: lib/who_need_help_web/live/request_live/index.ex:102 -#: lib/who_need_help_web/live/request_live/index.ex:103 -#: lib/who_need_help_web/live/request_live/show.ex:533 +#: lib/who_need_help_web/live/activity_live/index.ex:166 +#: lib/who_need_help_web/live/activity_live/show.ex:338 +#: lib/who_need_help_web/live/moderation_live.ex:257 +#: lib/who_need_help_web/live/request_live/index.ex:176 +#: lib/who_need_help_web/live/request_live/index.ex:177 +#: lib/who_need_help_web/live/request_live/show.ex:631 #, elixir-autogen, elixir-format msgid "Open" msgstr "Відкрита" -#: lib/who_need_help_web/live/moderation_live.ex:420 +#: lib/who_need_help_web/live/moderation_live.ex:424 #, elixir-autogen, elixir-format msgid "Open abuse signals" msgstr "Відкриті сигнали про зловживання" @@ -1454,40 +1399,40 @@ msgstr "Відкриті сигнали про зловживання" msgid "Open proposals" msgstr "Відкриті пропозиції" -#: lib/who_need_help_web/live/profile_live.ex:171 +#: lib/who_need_help_web/live/profile_live.ex:174 #, elixir-autogen, elixir-format msgid "Optional external thank-you link" msgstr "Необов’язкове зовнішнє посилання для подяки" -#: lib/who_need_help_web/live/request_live/show.ex:900 +#: lib/who_need_help_web/live/request_live/show.ex:1011 #, elixir-autogen, elixir-format msgid "Optional thank-you link" msgstr "Необов’язкове посилання для подяки" -#: lib/who_need_help_web/live/activity_live/show.ex:371 +#: lib/who_need_help_web/live/activity_live/show.ex:390 #, elixir-autogen, elixir-format msgid "Organizer" msgstr "Організатор" -#: lib/who_need_help_web/live/activity_live/show.ex:160 +#: lib/who_need_help_web/live/activity_live/show.ex:155 #, elixir-autogen, elixir-format msgid "Organizer blocked. Their activities and messages are hidden." msgstr "Організатора заблоковано. Його активності й повідомлення приховано." -#: lib/who_need_help_web/live/activity_live/show.ex:520 +#: lib/who_need_help_web/live/activity_live/show.ex:539 #, elixir-autogen, elixir-format msgid "Organizer controls" msgstr "Керування організатора" -#: lib/who_need_help_web/live/activity_live/show.ex:380 +#: lib/who_need_help_web/live/activity_live/show.ex:399 #, elixir-autogen, elixir-format msgid "Organizer social links" msgstr "Посилання організатора на соцмережі" -#: lib/who_need_help_web/live/activity_live/show.ex:648 -#: lib/who_need_help_web/live/moderation_live.ex:250 -#: lib/who_need_help_web/live/profile_live.ex:308 -#: lib/who_need_help_web/live/request_live/show.ex:979 +#: lib/who_need_help_web/live/activity_live/show.ex:667 +#: lib/who_need_help_web/live/moderation_live.ex:254 +#: lib/who_need_help_web/live/profile_live.ex:311 +#: lib/who_need_help_web/live/request_live/show.ex:1092 #, elixir-autogen, elixir-format msgid "Other" msgstr "Інше" @@ -1497,57 +1442,57 @@ msgstr "Інше" msgid "Parent (optional)" msgstr "Батьківська категорія (необов’язково)" -#: lib/who_need_help_web/live/activity_live/show.ex:440 +#: lib/who_need_help_web/live/activity_live/show.ex:459 #, elixir-autogen, elixir-format msgid "Participant" msgstr "Учасник" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:64 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97 #, elixir-autogen, elixir-format msgid "Password" msgstr "Пароль" -#: lib/who_need_help_web/controllers/user_settings_controller.ex:47 +#: lib/who_need_help_web/controllers/user_settings_controller.ex:60 #, elixir-autogen, elixir-format msgid "Password updated successfully." msgstr "Пароль успішно оновлено." -#: lib/who_need_help_web/live/request_live/new.ex:278 +#: lib/who_need_help_web/live/request_live/new.ex:280 #, elixir-autogen, elixir-format msgid "Pickup instructions (optional)" msgstr "Інструкції для отримання (необов’язково)" -#: lib/who_need_help_web/live/request_live/show.ex:578 +#: lib/who_need_help_web/live/request_live/show.ex:686 #, elixir-autogen, elixir-format msgid "Pickup notes" msgstr "Інструкції з отримання" -#: lib/who_need_help_web/live/activity_live/new.ex:223 +#: lib/who_need_help_web/live/activity_live/new.ex:225 #, elixir-autogen, elixir-format msgid "Plan and expectations" msgstr "План і очікування" -#: lib/who_need_help_web/live/activity_live/show.ex:402 +#: lib/who_need_help_web/live/activity_live/show.ex:421 #, elixir-autogen, elixir-format msgid "Plan details" msgstr "Деталі плану" -#: lib/who_need_help_web/live/request_live/show.ex:520 +#: lib/who_need_help_web/live/request_live/show.ex:619 #, elixir-autogen, elixir-format msgid "Please check the entered data." msgstr "Будь ласка, перевірте введені дані." -#: lib/who_need_help_web/live/moderation_live.ex:235 +#: lib/who_need_help_web/live/moderation_live.ex:240 #, elixir-autogen, elixir-format msgid "Please check the submitted fields." msgstr "Будь ласка, перевірте вказані поля." -#: lib/who_need_help_web/live/activity_live/show.ex:314 +#: lib/who_need_help_web/live/activity_live/show.ex:334 #, elixir-autogen, elixir-format msgid "Please check the submitted message." msgstr "Будь ласка, перевірте надіслане повідомлення." -#: lib/who_need_help_web/live/request_live/new.ex:305 +#: lib/who_need_help_web/live/request_live/new.ex:307 #, elixir-autogen, elixir-format msgid "Podil, near the central pharmacy" msgstr "Поділь, поблизу центральної аптеки." @@ -1557,12 +1502,13 @@ msgstr "Поділь, поблизу центральної аптеки." msgid "Post a clear request" msgstr "Опублікуйте зрозумілу заявку" -#: lib/who_need_help_web/components/layouts.ex:45 +#: lib/who_need_help_web/components/layouts.ex:55 +#: lib/who_need_help_web/components/layouts.ex:87 #, elixir-autogen, elixir-format msgid "Primary navigation" msgstr "Основна навігація" -#: lib/who_need_help_web/live/request_live/show.ex:647 +#: lib/who_need_help_web/live/request_live/show.ex:755 #, elixir-autogen, elixir-format msgid "Private match chat" msgstr "Приватний чат учасників" @@ -1579,8 +1525,8 @@ msgstr "" msgid "Profile updated." msgstr "Профіль оновлено." -#: lib/who_need_help_web/live/moderation_live.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:976 +#: lib/who_need_help_web/live/moderation_live.ex:251 +#: lib/who_need_help_web/live/request_live/show.ex:1089 #, elixir-autogen, elixir-format msgid "Prohibited item" msgstr "Заборонений предмет" @@ -1624,29 +1570,29 @@ msgstr "" " одним учасником іншого; похідні ознаки руху й близькості можуть " "зберігатися." -#: lib/who_need_help_web/live/activity_live/new.ex:254 +#: lib/who_need_help_web/live/activity_live/new.ex:256 #, elixir-autogen, elixir-format msgid "Public map visibility" msgstr "Видимість на публічній мапі" -#: lib/who_need_help_web/live/profile_live.ex:314 +#: lib/who_need_help_web/live/profile_live.ex:317 #, elixir-autogen, elixir-format msgid "Public profile URL" msgstr "URL публічного профілю" -#: lib/who_need_help_web/live/profile_live.ex:182 +#: lib/who_need_help_web/live/profile_live.ex:185 #, elixir-autogen, elixir-format msgid "Public reputation" msgstr "Публічна репутація" -#: lib/who_need_help_web/live/activity_live/new.ex:115 +#: lib/who_need_help_web/live/activity_live/new.ex:117 #, elixir-autogen, elixir-format msgid "Publish a clear public plan. Join requests stay pending until you approve them." msgstr "" "Опублікуйте зрозумілий публічний план. Заявки на участь очікуватимуть, доки " "ви їх не схвалите." -#: lib/who_need_help_web/live/activity_live/new.ex:301 +#: lib/who_need_help_web/live/activity_live/new.ex:310 #, elixir-autogen, elixir-format msgid "Publish activity" msgstr "Опублікувати активність" @@ -1656,13 +1602,13 @@ msgstr "Опублікувати активність" msgid "Publish proposal" msgstr "Оприлюднити пропозицію" -#: lib/who_need_help_web/live/request_live/new.ex:355 +#: lib/who_need_help_web/live/request_live/new.ex:364 #, elixir-autogen, elixir-format msgid "Publish request" msgstr "Опублікувати заявку" -#: lib/who_need_help_web/live/activity_live/new.ex:299 -#: lib/who_need_help_web/live/request_live/new.ex:353 +#: lib/who_need_help_web/live/activity_live/new.ex:308 +#: lib/who_need_help_web/live/request_live/new.ex:362 #, elixir-autogen, elixir-format msgid "Publishing…" msgstr "Публікація…" @@ -1672,7 +1618,7 @@ msgstr "Публікація…" msgid "READ BEFORE USING THE SERVICE" msgstr "ПРОЧИТАЙТЕ ПЕРЕД ВИКОРИСТАННЯМ СЕРВІСУ" -#: lib/who_need_help_web/live/moderation_live.ex:286 +#: lib/who_need_help_web/live/moderation_live.ex:290 #, elixir-autogen, elixir-format msgid "RESTRICTED WORKSPACE" msgstr "ОБМЕЖЕНА РОБОЧА ОБЛАСТЬ" @@ -1691,17 +1637,17 @@ msgstr "" "передачі. Повторна допомога між однією парою не збільшує основний показник." #: lib/who_need_help_web/live/leaderboard_live.ex:60 -#: lib/who_need_help_web/live/request_live/show.ex:712 +#: lib/who_need_help_web/live/request_live/show.ex:823 #, elixir-autogen, elixir-format msgid "Rating" msgstr "Оцінка" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40 #, elixir-autogen, elixir-format msgid "Read the" msgstr "Прочитайте" -#: lib/who_need_help_web/live/activity_live/show.ex:641 +#: lib/who_need_help_web/live/activity_live/show.ex:660 #, elixir-autogen, elixir-format msgid "Reason" msgstr "Причина" @@ -1711,38 +1657,38 @@ msgstr "Причина" msgid "Register for an account" msgstr "Зареєструвати обліковий запис" -#: lib/who_need_help_web/live/moderation_live.ex:531 +#: lib/who_need_help_web/live/moderation_live.ex:535 #, elixir-autogen, elixir-format msgid "Reject" msgstr "Відхилити" -#: lib/who_need_help_web/live/moderation_live.ex:530 +#: lib/who_need_help_web/live/moderation_live.ex:534 #, elixir-autogen, elixir-format msgid "Rejection note" msgstr "Примітка про відхилення" -#: lib/who_need_help_web/live/profile_live.ex:292 +#: lib/who_need_help_web/live/profile_live.ex:295 #, elixir-autogen, elixir-format msgid "Remove" msgstr "Вилучити" -#: lib/who_need_help_web/live/activity_live/show.ex:449 +#: lib/who_need_help_web/live/activity_live/show.ex:468 #, elixir-autogen, elixir-format msgid "Report" msgstr "Поскаржитися" -#: lib/who_need_help_web/live/request_live/show.ex:681 +#: lib/who_need_help_web/live/request_live/show.ex:789 #, elixir-autogen, elixir-format msgid "Report message" msgstr "Поскаржитися на повідомлення" -#: lib/who_need_help_web/live/request_live/show.ex:971 +#: lib/who_need_help_web/live/request_live/show.ex:1084 #, elixir-autogen, elixir-format msgid "Report reason" msgstr "Причина скарги" -#: lib/who_need_help_web/live/activity_live/show.ex:128 -#: lib/who_need_help_web/live/request_live/show.ex:231 +#: lib/who_need_help_web/live/activity_live/show.ex:123 +#: lib/who_need_help_web/live/request_live/show.ex:261 #, elixir-autogen, elixir-format msgid "Report sent to moderators." msgstr "Скаргу надіслано модераторам." @@ -1752,29 +1698,29 @@ msgstr "Скаргу надіслано модераторам." msgid "Report updated." msgstr "Скаргу оновлено." -#: lib/who_need_help_web/live/moderation_live.ex:386 +#: lib/who_need_help_web/live/moderation_live.ex:390 #, elixir-autogen, elixir-format msgid "Reported evidence" msgstr "Матеріали зі скарги" -#: lib/who_need_help_web/live/moderation_live.ex:293 +#: lib/who_need_help_web/live/moderation_live.ex:297 #, elixir-autogen, elixir-format msgid "Reports" msgstr "Скарги" -#: lib/who_need_help_web/live/request_live/show.ex:940 +#: lib/who_need_help_web/live/request_live/show.ex:1053 #, elixir-autogen, elixir-format msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages." msgstr "" "Скарги надходять модераторам. Блокування приховує заявки в обох напрямках і " "не дає надсилати нові повідомлення." -#: lib/who_need_help_web/live/request_live/show.ex:102 +#: lib/who_need_help_web/live/request_live/show.ex:127 #, elixir-autogen, elixir-format msgid "Request cancelled." msgstr "Заявку скасовано." -#: lib/who_need_help_web/live/request_live/new.ex:299 +#: lib/who_need_help_web/live/request_live/new.ex:301 #, elixir-autogen, elixir-format msgid "Request expires (UTC)" msgstr "Запит закінчується (UTC)" @@ -1789,84 +1735,84 @@ msgstr "Заявку приховано." msgid "Request restored." msgstr "Заявку відновлено." -#: lib/who_need_help_web/live/activity_live/show.ex:502 +#: lib/who_need_help_web/live/activity_live/show.ex:521 #, elixir-autogen, elixir-format msgid "Request to join" msgstr "Подати заявку на участь" -#: lib/who_need_help_web/live/request_live/show.ex:617 +#: lib/who_need_help_web/live/request_live/show.ex:725 #, elixir-autogen, elixir-format msgid "Requester links:" msgstr "Посилання заявника:" -#: lib/who_need_help_web/live/request_live/show.ex:598 +#: lib/who_need_help_web/live/request_live/show.ex:706 #, elixir-autogen, elixir-format msgid "Requester:" msgstr "Заявник:" -#: lib/who_need_help_web/live/request_live/show.ex:632 +#: lib/who_need_help_web/live/request_live/show.ex:740 #, elixir-autogen, elixir-format msgid "Requester: %{count} completed" msgstr "Автор заявки: %{count} завершено" -#: lib/who_need_help_web/live/moderation_live.ex:367 +#: lib/who_need_help_web/live/moderation_live.ex:371 #, elixir-autogen, elixir-format msgid "Resolution note" msgstr "Примітка до рішення" -#: lib/who_need_help_web/live/moderation_live.ex:255 -#: lib/who_need_help_web/live/moderation_live.ex:361 +#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:365 #, elixir-autogen, elixir-format msgid "Resolved" msgstr "Вирішено" -#: lib/who_need_help_web/live/moderation_live.ex:262 -#: lib/who_need_help_web/live/moderation_live.ex:611 +#: lib/who_need_help_web/live/moderation_live.ex:266 +#: lib/who_need_help_web/live/moderation_live.ex:615 #, elixir-autogen, elixir-format msgid "Restricted" msgstr "Обмежено" -#: lib/who_need_help_web/live/moderation_live.ex:451 +#: lib/who_need_help_web/live/moderation_live.ex:455 #, elixir-autogen, elixir-format msgid "Review note" msgstr "Примітка до перевірки" -#: lib/who_need_help_web/live/request_live/show.ex:203 +#: lib/who_need_help_web/live/request_live/show.ex:233 #, elixir-autogen, elixir-format msgid "Review saved. It appears after both participants review." msgstr "" "Відгук збережено. Він з’явиться після того, як відгуки залишать обидва " "учасники." -#: lib/who_need_help_web/live/moderation_live.ex:257 -#: lib/who_need_help_web/live/moderation_live.ex:447 +#: lib/who_need_help_web/live/moderation_live.ex:261 +#: lib/who_need_help_web/live/moderation_live.ex:451 #, elixir-autogen, elixir-format msgid "Reviewed" msgstr "Перевірено" -#: lib/who_need_help_web/live/moderation_live.ex:254 -#: lib/who_need_help_web/live/moderation_live.ex:360 +#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:364 #, elixir-autogen, elixir-format msgid "Reviewing" msgstr "На розгляді" -#: lib/who_need_help_web/live/moderation_live.ex:571 +#: lib/who_need_help_web/live/moderation_live.ex:575 #, elixir-autogen, elixir-format msgid "Role" msgstr "Роль" -#: lib/who_need_help_web/live/moderation_live.ex:510 +#: lib/who_need_help_web/live/moderation_live.ex:514 #, elixir-autogen, elixir-format msgid "Russian description (optional)" msgstr "Опис російською (необов’язково)" -#: lib/who_need_help_web/live/moderation_live.ex:498 +#: lib/who_need_help_web/live/moderation_live.ex:502 #, elixir-autogen, elixir-format msgid "Russian name (optional)" msgstr "Назва російською (необов’язково)" -#: lib/who_need_help_web/live/activity_live/show.ex:616 -#: lib/who_need_help_web/live/request_live/show.ex:938 +#: lib/who_need_help_web/live/activity_live/show.ex:635 +#: lib/who_need_help_web/live/request_live/show.ex:1051 #, elixir-autogen, elixir-format msgid "Safety controls" msgstr "Інструменти безпеки" @@ -1879,15 +1825,15 @@ msgstr "" "публічний запуск і кожна зміна production-інфраструктури залишаються під " "контролем людей." -#: lib/who_need_help_web/components/layouts.ex:104 +#: lib/who_need_help_web/components/layouts.ex:145 #: lib/who_need_help_web/controllers/page_html/safety.html.heex:6 #, elixir-autogen, elixir-format msgid "Safety rules" msgstr "Правила безпеки" -#: lib/who_need_help_web/live/moderation_live.ex:369 -#: lib/who_need_help_web/live/moderation_live.ex:452 -#: lib/who_need_help_web/live/moderation_live.ex:619 +#: lib/who_need_help_web/live/moderation_live.ex:373 +#: lib/who_need_help_web/live/moderation_live.ex:456 +#: lib/who_need_help_web/live/moderation_live.ex:623 #, elixir-autogen, elixir-format msgid "Save" msgstr "Зберегти" @@ -1897,29 +1843,29 @@ msgstr "Зберегти" msgid "Save Password" msgstr "Зберегти пароль" -#: lib/who_need_help_web/live/profile_live.ex:177 +#: lib/who_need_help_web/live/profile_live.ex:180 #, elixir-autogen, elixir-format msgid "Save profile" msgstr "Зберегти профіль" -#: lib/who_need_help_web/live/request_live/index.ex:98 -#: lib/who_need_help_web/live/request_live/index.ex:99 -#: lib/who_need_help_web/live/request_live/index.ex:170 -#: lib/who_need_help_web/live/request_live/new.ex:293 -#: lib/who_need_help_web/live/request_live/show.ex:529 -#: lib/who_need_help_web/live/request_live/show.ex:530 +#: lib/who_need_help_web/live/request_live/index.ex:172 +#: lib/who_need_help_web/live/request_live/index.ex:173 +#: lib/who_need_help_web/live/request_live/index.ex:245 +#: lib/who_need_help_web/live/request_live/new.ex:295 +#: lib/who_need_help_web/live/request_live/show.ex:627 +#: lib/who_need_help_web/live/request_live/show.ex:628 #, elixir-autogen, elixir-format msgid "Scheduled" msgstr "Заплановано" -#: lib/who_need_help_web/live/activity_live/show.ex:467 -#: lib/who_need_help_web/live/request_live/show.ex:696 +#: lib/who_need_help_web/live/activity_live/show.ex:486 +#: lib/who_need_help_web/live/request_live/show.ex:806 #, elixir-autogen, elixir-format msgid "Send" msgstr "Надіслати" -#: lib/who_need_help_web/live/activity_live/show.ex:657 -#: lib/who_need_help_web/live/request_live/show.ex:988 +#: lib/who_need_help_web/live/activity_live/show.ex:676 +#: lib/who_need_help_web/live/request_live/show.ex:1101 #, elixir-autogen, elixir-format msgid "Send report" msgstr "Надіслати скаргу" @@ -1931,30 +1877,25 @@ msgstr "" "Установіть CODEX_SESSION_ID у середовищі розгортання перед записом " "демонстрації." -#: lib/who_need_help_web/live/request_live/show.ex:926 +#: lib/who_need_help_web/live/request_live/show.ex:1038 #, elixir-autogen, elixir-format msgid "Share location" msgstr "Поділитися геопозицією" -#: lib/who_need_help_web/live/request_live/new.ex:280 +#: lib/who_need_help_web/live/request_live/new.ex:282 #, elixir-autogen, elixir-format msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat." msgstr "" "Повідомляйте лише нечутливі інструкції. Точні відомості можна передати в " "чаті після призначення помічника." -#: lib/who_need_help_web/live/request_live/show.ex:501 -#, elixir-autogen, elixir-format -msgid "Shared live location" -msgstr "Геопозиція, що передається" - -#: lib/who_need_help_web/live/profile_live.ex:147 +#: lib/who_need_help_web/live/profile_live.ex:150 #, elixir-autogen, elixir-format msgid "Short bio" msgstr "Коротко про себе" -#: lib/who_need_help_web/live/activity_live/new.ex:217 -#: lib/who_need_help_web/live/request_live/new.ex:264 +#: lib/who_need_help_web/live/activity_live/new.ex:219 +#: lib/who_need_help_web/live/request_live/new.ex:266 #, elixir-autogen, elixir-format msgid "Short title" msgstr "Короткий заголовок" @@ -1969,12 +1910,12 @@ msgstr "Зареєструватися" msgid "Signal updated." msgstr "Сигнал оновлено." -#: lib/who_need_help_web/components/layouts/root.html.heex:52 +#: lib/who_need_help_web/components/layouts/root.html.heex:27 #, elixir-autogen, elixir-format msgid "Skip to main content" msgstr "Перейти до головного вмісту" -#: lib/who_need_help_web/live/moderation_live.ex:490 +#: lib/who_need_help_web/live/moderation_live.ex:494 #, elixir-autogen, elixir-format msgid "Slug" msgstr "Слаг" @@ -1995,12 +1936,12 @@ msgstr "Спільна активність" msgid "Social link added as unverified." msgstr "Посилання на соцмережу додано як непідтверджене." -#: lib/who_need_help_web/live/profile_live.ex:67 +#: lib/who_need_help_web/live/profile_live.ex:74 #, elixir-autogen, elixir-format msgid "Social link was not found." msgstr "Посилання на соцмережу не знайдено." -#: lib/who_need_help_web/live/profile_live.ex:230 +#: lib/who_need_help_web/live/profile_live.ex:233 #, elixir-autogen, elixir-format msgid "Social links" msgstr "Соціальні посилання" @@ -2010,54 +1951,54 @@ msgstr "Соціальні посилання" msgid "Social verification could not be started." msgstr "Не вдалося розпочати підтвердження соцмережі." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:77 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:88 #, elixir-autogen, elixir-format msgid "Social verification failed. Please retry." msgstr "Не вдалося підтвердити соцмережу. Спробуйте ще раз." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:58 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 #, elixir-autogen, elixir-format msgid "Social verification session expired. Please retry." msgstr "Сеанс підтвердження соцмережі завершився. Спробуйте ще раз." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:65 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:76 #, elixir-autogen, elixir-format msgid "Social verification session is invalid." msgstr "Сеанс соціальної перевірки є некоректним." -#: lib/who_need_help_web/live/activity_live/show.ex:646 -#: lib/who_need_help_web/live/moderation_live.ex:248 -#: lib/who_need_help_web/live/request_live/show.ex:977 +#: lib/who_need_help_web/live/activity_live/show.ex:665 +#: lib/who_need_help_web/live/moderation_live.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:1090 #, elixir-autogen, elixir-format msgid "Spam" msgstr "Спам" -#: lib/who_need_help_web/live/request_live/show.ex:829 +#: lib/who_need_help_web/live/request_live/show.ex:940 #, elixir-autogen, elixir-format msgid "Start helping" msgstr "Почати допомагати" -#: lib/who_need_help_web/live/activity_live/show.ex:355 +#: lib/who_need_help_web/live/activity_live/show.ex:374 #, elixir-autogen, elixir-format msgid "Starts" msgstr "Початок" -#: lib/who_need_help_web/live/activity_live/new.ex:232 +#: lib/who_need_help_web/live/activity_live/new.ex:234 #, elixir-autogen, elixir-format msgid "Starts at (UTC)" msgstr "Початок (UTC)" -#: lib/who_need_help_web/live/moderation_live.ex:572 +#: lib/who_need_help_web/live/moderation_live.ex:576 #, elixir-autogen, elixir-format msgid "Status" msgstr "Статус" -#: lib/who_need_help_web/live/request_live/show.ex:933 +#: lib/who_need_help_web/live/request_live/show.ex:1046 #, elixir-autogen, elixir-format msgid "Stop and delete position" msgstr "Зупинити й видалити геопозицію" -#: lib/who_need_help_web/live/moderation_live.ex:515 +#: lib/who_need_help_web/live/moderation_live.ex:519 #, elixir-autogen, elixir-format msgid "Structured fields JSON" msgstr "Структуровані поля JSON" @@ -2067,34 +2008,34 @@ msgstr "Структуровані поля JSON" msgid "Structured fields must be a valid JSON object." msgstr "Структуровані поля мають бути коректним об’єктом JSON." -#: lib/who_need_help_web/live/request_live/show.ex:726 +#: lib/who_need_help_web/live/request_live/show.ex:837 #, elixir-autogen, elixir-format msgid "Submit review" msgstr "Надіслати відгук" -#: lib/who_need_help_web/live/moderation_live.ex:263 -#: lib/who_need_help_web/live/moderation_live.ex:612 +#: lib/who_need_help_web/live/moderation_live.ex:267 +#: lib/who_need_help_web/live/moderation_live.ex:616 #, elixir-autogen, elixir-format msgid "Suspended" msgstr "Заблоковано" -#: lib/who_need_help_web/live/request_live/show.ex:509 +#: lib/who_need_help_web/live/request_live/show.ex:604 #, elixir-autogen, elixir-format msgid "That handover code is not correct." msgstr "Неправильний код передачі." -#: lib/who_need_help_web/live/activity_live/show.ex:145 -#: lib/who_need_help_web/live/request_live/show.ex:248 +#: lib/who_need_help_web/live/activity_live/show.ex:140 +#: lib/who_need_help_web/live/request_live/show.ex:278 #, elixir-autogen, elixir-format msgid "That message cannot be reported from this view." msgstr "На це повідомлення не можна поскаржитися з цього екрана." -#: lib/who_need_help_web/controllers/social_oauth_controller.ex:69 +#: lib/who_need_help_web/controllers/social_oauth_controller.ex:80 #, elixir-autogen, elixir-format msgid "That social account is already linked." msgstr "Цей обліковий запис соцмережі вже прив’язано." -#: lib/who_need_help_web/live/request_live/show.ex:513 +#: lib/who_need_help_web/live/request_live/show.ex:608 #, elixir-autogen, elixir-format msgid "That step is not available in the current state." msgstr "Цей крок недоступний у поточному стані." @@ -2109,27 +2050,27 @@ msgstr "Провайдер перевірки не налаштовано." msgid "That verification provider is not supported." msgstr "Цей постачальник перевірки не підтримується." -#: lib/who_need_help_web/live/activity_live/show.ex:299 +#: lib/who_need_help_web/live/activity_live/show.ex:312 #, elixir-autogen, elixir-format msgid "The approved group has reached its capacity." msgstr "Схвалена група вже заповнена." -#: lib/who_need_help_web/live/request_live/show.ex:180 +#: lib/who_need_help_web/live/request_live/show.ex:208 #, elixir-autogen, elixir-format msgid "The browser could not share your location." msgstr "Браузеру не вдалося передати вашу геопозицію." -#: lib/who_need_help_web/live/moderation_live.ex:233 +#: lib/who_need_help_web/live/moderation_live.ex:237 #, elixir-autogen, elixir-format msgid "The last administrator cannot demote themselves." msgstr "Останній адміністратор не може понизити власну роль." -#: lib/who_need_help_web/controllers/user_session_controller.ex:31 +#: lib/who_need_help_web/controllers/user_session_controller.ex:37 #, elixir-autogen, elixir-format msgid "The link is invalid or it has expired." msgstr "Посилання недійсне або прострочене." -#: lib/who_need_help_web/live/activity_live/show.ex:507 +#: lib/who_need_help_web/live/activity_live/show.ex:526 #, elixir-autogen, elixir-format msgid "The organizer has not approved your request yet." msgstr "Організатор ще не схвалив ваш запит." @@ -2140,59 +2081,59 @@ msgid "The pharmacy closes soon and I cannot leave home. The item is already pai msgstr "" "Аптека скоро зачиняється, а я не можу вийти з дому. Товар уже оплачено." -#: lib/who_need_help_web/live/profile_live.ex:175 +#: lib/who_need_help_web/live/profile_live.ex:178 #, elixir-autogen, elixir-format msgid "The platform never handles, requires, splits, or guarantees a payment." msgstr "" "Платформа ніколи не обробляє, не вимагає, не розподіляє й не гарантує " "оплату." -#: lib/who_need_help_web/live/activity_live/show.ex:489 +#: lib/who_need_help_web/live/activity_live/show.ex:508 #, elixir-autogen, elixir-format msgid "The public map is approximate or hidden. Exact coordinates appear only after approval." msgstr "" "Публічна карта показує приблизну область або прихована. Точні координати " "з’являються лише після схвалення." -#: lib/who_need_help_web/live/activity_live/show.ex:629 +#: lib/who_need_help_web/live/activity_live/show.ex:648 #, elixir-autogen, elixir-format msgid "The report is scoped to the selected group message." msgstr "Скарга стосується вибраного повідомлення групи." -#: lib/who_need_help_web/live/request_live/show.ex:963 +#: lib/who_need_help_web/live/request_live/show.ex:1076 #, elixir-autogen, elixir-format msgid "The report will be scoped to the selected chat message." msgstr "Скарга стосуватиметься вибраного повідомлення чату." -#: lib/who_need_help_web/live/activity_live/show.ex:302 +#: lib/who_need_help_web/live/activity_live/show.ex:315 #, elixir-autogen, elixir-format msgid "This action is unavailable." msgstr "Ця дія недоступна." -#: lib/who_need_help_web/live/activity_live/show.ex:309 +#: lib/who_need_help_web/live/activity_live/show.ex:322 #, elixir-autogen, elixir-format msgid "This activity is no longer open." msgstr "Ця активність більше не відкрита." -#: lib/who_need_help_web/live/activity_live/show.ex:307 +#: lib/who_need_help_web/live/activity_live/show.ex:320 #, elixir-autogen, elixir-format msgid "This activity state has already changed." msgstr "Цей стан активності вже змінено." -#: lib/who_need_help_web/live/request_live/show.ex:518 +#: lib/who_need_help_web/live/request_live/show.ex:613 #, elixir-autogen, elixir-format msgid "This interaction is blocked." msgstr "Ця взаємодія заблокована." -#: lib/who_need_help_web/live/request_live/index.ex:136 -#: lib/who_need_help_web/live/request_live/show.ex:557 +#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/request_live/show.ex:665 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgstr "" "Це не екстрена служба. За безпосередньої небезпеки зверніться до місцевих " "екстрених служб." -#: lib/who_need_help_web/live/request_live/new.ex:162 +#: lib/who_need_help_web/live/request_live/new.ex:164 #, elixir-autogen, elixir-format msgid "This is not an emergency service. Contact local emergency services for immediate danger. Never include prescription details, payment card data, access codes, or other sensitive information." msgstr "" @@ -2200,7 +2141,7 @@ msgstr "" "екстрених служб. Ніколи не вказуйте дані рецепта чи платіжної картки, коди " "доступу та іншу чутливу інформацію." -#: lib/who_need_help_web/live/moderation_live.ex:227 +#: lib/who_need_help_web/live/moderation_live.ex:231 #, elixir-autogen, elixir-format msgid "This proposal has already been reviewed." msgstr "Цю пропозицію вже розглянуто." @@ -2210,67 +2151,62 @@ msgstr "Цю пропозицію вже розглянуто." msgid "This proposal is already closed." msgstr "Ця пропозиція вже закрита." -#: lib/who_need_help_web/live/moderation_live.ex:406 +#: lib/who_need_help_web/live/moderation_live.ex:410 #, elixir-autogen, elixir-format msgid "This report has no linked conversation." msgstr "Ця скарга не має пов’язаного листування." -#: lib/who_need_help_web/live/request_live/show.ex:905 +#: lib/who_need_help_web/live/request_live/show.ex:1016 #, elixir-autogen, elixir-format msgid "This request already has a helper." msgstr "Ця заявка вже має помічника." -#: lib/who_need_help_web/live/request_live/show.ex:52 +#: lib/who_need_help_web/live/request_live/show.ex:74 +#: lib/who_need_help_web/live/request_live/show.ex:615 #, elixir-autogen, elixir-format msgid "This request is no longer available." msgstr "Ця заявка більше не доступна." -#: lib/who_need_help_web/live/request_live/show.ex:37 +#: lib/who_need_help_web/live/request_live/show.ex:59 #, elixir-autogen, elixir-format msgid "This request is unavailable." msgstr "Ця заявка недоступна." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:66 -#, elixir-autogen, elixir-format -msgid "Tip: If you prefer passwords, you can enable them in the user settings." -msgstr "" -"Порада: якщо вам зручніше користуватися паролем, його можна ввімкнути в " -"налаштуваннях." - #: lib/who_need_help_web/controllers/user_session_html/new.html.heex:27 #, elixir-autogen, elixir-format msgid "To see sent emails, visit" msgstr "Щоб переглянути надіслані листи, відкрийте" -#: lib/who_need_help_web/live/request_live/index.ex:96 -#: lib/who_need_help_web/live/request_live/index.ex:97 -#: lib/who_need_help_web/live/request_live/index.ex:169 -#: lib/who_need_help_web/live/request_live/new.ex:292 -#: lib/who_need_help_web/live/request_live/show.ex:527 -#: lib/who_need_help_web/live/request_live/show.ex:528 +#: lib/who_need_help_web/live/request_live/index.ex:170 +#: lib/who_need_help_web/live/request_live/index.ex:171 +#: lib/who_need_help_web/live/request_live/index.ex:244 +#: lib/who_need_help_web/live/request_live/new.ex:294 +#: lib/who_need_help_web/live/request_live/show.ex:625 +#: lib/who_need_help_web/live/request_live/show.ex:626 #, elixir-autogen, elixir-format msgid "Today" msgstr "Сьогодні" -#: lib/who_need_help_web/live/activity_live/new.ex:100 -#: lib/who_need_help_web/live/activity_live/show.ex:312 +#: lib/who_need_help_web/live/activity_live/new.ex:103 +#: lib/who_need_help_web/live/activity_live/show.ex:332 #: lib/who_need_help_web/live/category_proposal_live.ex:54 -#: lib/who_need_help_web/live/request_live/show.ex:519 +#: lib/who_need_help_web/live/profile_live.ex:59 +#: lib/who_need_help_web/live/request_live/show.ex:618 #, elixir-autogen, elixir-format msgid "Too many actions in the configured time window." msgstr "Забагато дій протягом налаштованого проміжку часу." -#: lib/who_need_help_web/controllers/user_registration_controller.ex:45 +#: lib/who_need_help_web/controllers/user_registration_controller.ex:49 #, elixir-autogen, elixir-format msgid "Too many registration attempts in the configured time window." msgstr "Забагато спроб реєстрації протягом налаштованого проміжку часу." -#: lib/who_need_help_web/live/request_live/new.ex:139 +#: lib/who_need_help_web/live/request_live/new.ex:142 #, elixir-autogen, elixir-format msgid "Too many requests in the configured time window." msgstr "Забагато запитів протягом налаштованого проміжку часу." -#: lib/who_need_help_web/controllers/user_session_controller.ex:79 +#: lib/who_need_help_web/controllers/user_session_controller.ex:102 #, elixir-autogen, elixir-format msgid "Too many sign-in emails in the configured time window." msgstr "Забагато листів для входу протягом налаштованого проміжку часу." @@ -2285,27 +2221,27 @@ msgstr "Верхній рівень" msgid "Total" msgstr "Загалом" -#: lib/who_need_help_web/live/activity_live/new.ex:244 +#: lib/who_need_help_web/live/activity_live/new.ex:246 #, elixir-autogen, elixir-format msgid "Total group capacity" msgstr "Загальний розмір групи" -#: lib/who_need_help_web/live/moderation_live.ex:506 +#: lib/who_need_help_web/live/moderation_live.ex:510 #, elixir-autogen, elixir-format msgid "Ukrainian description (optional)" msgstr "Опис українською (необов’язково)" -#: lib/who_need_help_web/live/moderation_live.ex:494 +#: lib/who_need_help_web/live/moderation_live.ex:498 #, elixir-autogen, elixir-format msgid "Ukrainian name (optional)" msgstr "Назва українською (необов’язково)" -#: lib/who_need_help_web/live/profile_live.ex:342 +#: lib/who_need_help_web/live/profile_live.ex:345 #, elixir-autogen, elixir-format msgid "Unblock" msgstr "Розблокувати" -#: lib/who_need_help_web/live/request_live/show.ex:956 +#: lib/who_need_help_web/live/request_live/show.ex:1069 #, elixir-autogen, elixir-format msgid "Unblock this user" msgstr "Розблокувати цього користувача" @@ -2320,8 +2256,8 @@ msgstr "Унікальні люди" msgid "Update email instructions" msgstr "Інструкції для зміни електронної пошти" -#: lib/who_need_help_web/live/request_live/index.ex:165 -#: lib/who_need_help_web/live/request_live/new.ex:289 +#: lib/who_need_help_web/live/request_live/index.ex:240 +#: lib/who_need_help_web/live/request_live/new.ex:291 #, elixir-autogen, elixir-format msgid "Urgency" msgstr "Терміновість" @@ -2332,25 +2268,25 @@ msgstr "Терміновість" msgid "Urgent help" msgstr "Термінова допомога" -#: lib/who_need_help_web/live/activity_live/new.ex:121 +#: lib/who_need_help_web/live/activity_live/new.ex:123 #, elixir-autogen, elixir-format msgid "Use a public first meeting point. Do not publish a home address, access code, travel document, or other sensitive information." msgstr "" "Призначайте першу зустріч у громадському місці. Не публікуйте домашню " "адресу, код доступу, документ для подорожі чи іншу чутливу інформацію." -#: lib/who_need_help_web/components/layouts.ex:194 +#: lib/who_need_help_web/components/layouts.ex:349 #, elixir-autogen, elixir-format msgid "Use dark theme" msgstr "Увімкнути темну тему" -#: lib/who_need_help_web/components/layouts.ex:185 +#: lib/who_need_help_web/components/layouts.ex:340 #, elixir-autogen, elixir-format msgid "Use light theme" msgstr "Увімкнути світлу тему" -#: lib/who_need_help_web/live/activity_live/new.ex:267 -#: lib/who_need_help_web/live/request_live/new.ex:325 +#: lib/who_need_help_web/live/activity_live/new.ex:271 +#: lib/who_need_help_web/live/request_live/new.ex:329 #, elixir-autogen, elixir-format msgid "Use my current foreground location" msgstr "Використати мою поточну геопозицію" @@ -2362,24 +2298,24 @@ msgstr "" "Користуйтеся приватним чатом і, за бажанням, передаванням геопозиції в " "реальному часі." -#: lib/who_need_help_web/components/layouts.ex:176 +#: lib/who_need_help_web/components/layouts.ex:331 #, elixir-autogen, elixir-format msgid "Use system theme" msgstr "Використати системну тему" -#: lib/who_need_help_web/live/moderation_live.ex:266 -#: lib/who_need_help_web/live/moderation_live.ex:570 -#: lib/who_need_help_web/live/moderation_live.ex:632 +#: lib/who_need_help_web/live/moderation_live.ex:270 +#: lib/who_need_help_web/live/moderation_live.ex:574 +#: lib/who_need_help_web/live/moderation_live.ex:636 #, elixir-autogen, elixir-format msgid "User" msgstr "Користувач" -#: lib/who_need_help_web/live/request_live/show.ex:271 +#: lib/who_need_help_web/live/request_live/show.ex:307 #, elixir-autogen, elixir-format msgid "User blocked. Their requests and new messages are hidden." msgstr "Користувача заблоковано. Його заявки й нові повідомлення приховано." -#: lib/who_need_help_web/controllers/user_session_controller.ex:19 +#: lib/who_need_help_web/controllers/user_session_controller.ex:25 #, elixir-autogen, elixir-format msgid "User confirmed successfully." msgstr "Користувача успішно підтверджено." @@ -2394,8 +2330,8 @@ msgstr "Роль користувача оновлено." msgid "User status updated." msgstr "Статус користувача оновлено." -#: lib/who_need_help_web/live/profile_live.ex:80 -#: lib/who_need_help_web/live/request_live/show.ex:285 +#: lib/who_need_help_web/live/profile_live.ex:87 +#: lib/who_need_help_web/live/request_live/show.ex:321 #, elixir-autogen, elixir-format msgid "User unblocked." msgstr "Користувача розблоковано." @@ -2405,12 +2341,12 @@ msgstr "Користувача розблоковано." msgid "Verified people" msgstr "Підтверджені люди" -#: lib/who_need_help_web/live/profile_live.ex:242 +#: lib/who_need_help_web/live/profile_live.ex:245 #, elixir-autogen, elixir-format msgid "Verify GitHub" msgstr "Підтвердити через GitHub" -#: lib/who_need_help_web/live/request_live/show.ex:885 +#: lib/who_need_help_web/live/request_live/show.ex:996 #, elixir-autogen, elixir-format msgid "Verify handover" msgstr "Підтвердити передачу" @@ -2420,28 +2356,23 @@ msgstr "Підтвердити передачу" msgid "Verify the handover" msgstr "Підтвердьте передачу" -#: lib/who_need_help_web/live/moderation_live.ex:323 +#: lib/who_need_help_web/live/moderation_live.ex:327 #, elixir-autogen, elixir-format msgid "View scoped evidence" msgstr "Переглянути пов’язані докази" -#: lib/who_need_help_web/live/profile_live.ex:207 +#: lib/who_need_help_web/live/profile_live.ex:210 #, elixir-autogen, elixir-format msgid "Visible reviews" msgstr "Відкриті відгуки" -#: lib/who_need_help_web/live/request_live/show.ex:755 +#: lib/who_need_help_web/live/request_live/show.ex:866 #, elixir-autogen, elixir-format msgid "Waiting for a nearby volunteer." msgstr "Очікуємо помічника поруч." -#: lib/who_need_help_web/controllers/user_session_html/confirm.html.heex:4 -#, elixir-autogen, elixir-format -msgid "Welcome %{email}" -msgstr "Ласкаво просимо %{email}" - -#: lib/who_need_help_web/controllers/user_session_controller.ex:20 -#: lib/who_need_help_web/controllers/user_session_controller.ex:40 +#: lib/who_need_help_web/controllers/user_session_controller.ex:26 +#: lib/who_need_help_web/controllers/user_session_controller.ex:51 #, elixir-autogen, elixir-format msgid "Welcome back!" msgstr "З поверненням!" @@ -2451,8 +2382,8 @@ msgstr "З поверненням!" msgid "What Codex contributed" msgstr "Що зробив Codex" -#: lib/who_need_help_web/live/activity_live/show.ex:654 -#: lib/who_need_help_web/live/request_live/show.ex:985 +#: lib/who_need_help_web/live/activity_live/show.ex:673 +#: lib/who_need_help_web/live/request_live/show.ex:1098 #, elixir-autogen, elixir-format msgid "What happened?" msgstr "Що сталося?" @@ -2462,7 +2393,7 @@ msgstr "Що сталося?" msgid "What help category is missing?" msgstr "Яка категорія допомоги відсутня?" -#: lib/who_need_help_web/live/request_live/new.ex:270 +#: lib/who_need_help_web/live/request_live/new.ex:272 #, elixir-autogen, elixir-format msgid "What help do you need?" msgstr "Яка допомога вам потрібна?" @@ -2483,12 +2414,12 @@ msgstr "" "перевірити кожну людину, заявку, річ, маршрут чи результат і не може " "гарантувати безпеку." -#: lib/who_need_help_web/components/layouts.ex:40 +#: lib/who_need_help_web/components/layouts.ex:43 #, elixir-autogen, elixir-format msgid "Who Need Help home" msgstr "Головна Who Need Help" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:54 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53 #, elixir-autogen, elixir-format msgid "Who Need Help is not an emergency or medical service. Medicine requests are limited to pickup of legal items already purchased or reserved." msgstr "" @@ -2503,7 +2434,7 @@ msgstr "" "через підписку ChatGPT розробника. Ключ OpenAI API та резервний варіант API " "з оплатою за використання не застосовуються." -#: lib/who_need_help_web/live/request_live/index.ex:123 +#: lib/who_need_help_web/live/request_live/index.ex:197 #, elixir-autogen, elixir-format msgid "Who needs help?" msgstr "Хто потребує допомоги?" @@ -2513,46 +2444,46 @@ msgstr "Хто потребує допомоги?" msgid "Why is this useful?" msgstr "Чому це корисно?" -#: lib/who_need_help_web/live/request_live/show.ex:846 +#: lib/who_need_help_web/live/request_live/show.ex:957 #, elixir-autogen, elixir-format msgid "Withdraw from this request" msgstr "Відмовитися від цієї заявки" -#: lib/who_need_help_web/live/request_live/show.ex:693 +#: lib/who_need_help_web/live/request_live/show.ex:802 #, elixir-autogen, elixir-format msgid "Write a safe coordination message…" msgstr "Напишіть безпечне повідомлення для координації…" -#: lib/who_need_help_web/live/activity_live/new.ex:202 -#: lib/who_need_help_web/live/activity_live/show.ex:281 -#: lib/who_need_help_web/live/request_live/new.ex:250 -#: lib/who_need_help_web/live/request_live/show.ex:470 +#: lib/who_need_help_web/live/activity_live/new.ex:204 +#: lib/who_need_help_web/live/activity_live/show.ex:294 +#: lib/who_need_help_web/live/request_live/new.ex:252 +#: lib/who_need_help_web/live/request_live/show.ex:561 #, elixir-autogen, elixir-format msgid "Yes" msgstr "Так" -#: lib/who_need_help_web/live/activity_live/show.ex:296 +#: lib/who_need_help_web/live/activity_live/show.ex:309 #, elixir-autogen, elixir-format msgid "You already have a join request for this activity." msgstr "Ви вже подали заявку на участь у цій активності." -#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#: lib/who_need_help_web/live/category_proposal_live.ex:58 #, elixir-autogen, elixir-format msgid "You already voted for this proposal." msgstr "Ви вже проголосували за цю пропозицію." -#: lib/who_need_help_web/live/activity_live/show.ex:487 +#: lib/who_need_help_web/live/activity_live/show.ex:506 #, elixir-autogen, elixir-format msgid "You are approved, so the map can show the exact meeting coordinates." msgstr "Вас схвалено, тому карта може показувати точні координати зустрічі." -#: lib/who_need_help_web/live/request_live/show.ex:92 +#: lib/who_need_help_web/live/request_live/show.ex:117 #, elixir-autogen, elixir-format msgid "You are matched. Use chat to coordinate safely." msgstr "Помічника призначено. Безпечно узгодьте деталі в чаті." -#: lib/who_need_help_web/live/activity_live/show.ex:304 -#: lib/who_need_help_web/live/request_live/show.ex:510 +#: lib/who_need_help_web/live/activity_live/show.ex:317 +#: lib/who_need_help_web/live/request_live/show.ex:605 #, elixir-autogen, elixir-format msgid "You are not allowed to do that." msgstr "Ви не маєте дозволу на цю дію." @@ -2562,44 +2493,44 @@ msgstr "Ви не маєте дозволу на цю дію." msgid "You are running the local mail adapter." msgstr "Використовується локальний поштовий адаптер." -#: lib/who_need_help_web/live/request_live/new.ex:328 +#: lib/who_need_help_web/live/request_live/new.ex:332 #, elixir-autogen, elixir-format msgid "You can also enter coordinates manually for local testing." msgstr "Ви також можете ввести координати вручну для локального тестування." -#: lib/who_need_help_web/live/request_live/index.ex:181 +#: lib/who_need_help_web/live/request_live/index.ex:256 #, elixir-autogen, elixir-format msgid "You can be the first person to ask the community." msgstr "Ви можете першими звернутися по допомогу до спільноти." -#: lib/who_need_help_web/live/request_live/show.ex:507 +#: lib/who_need_help_web/live/request_live/show.ex:602 #, elixir-autogen, elixir-format msgid "You cannot accept your own request." msgstr "Не можна прийняти власну заявку." -#: lib/who_need_help_web/live/activity_live/show.ex:303 +#: lib/who_need_help_web/live/activity_live/show.ex:316 #, elixir-autogen, elixir-format msgid "You cannot report your own content." msgstr "Не можна скаржитися на власний вміст." -#: lib/who_need_help_web/live/moderation_live.ex:230 +#: lib/who_need_help_web/live/moderation_live.ex:234 #, elixir-autogen, elixir-format msgid "You cannot restrict your own moderator account." msgstr "Не можна обмежити власний обліковий запис модератора." -#: lib/who_need_help_web/live/profile_live.ex:330 +#: lib/who_need_help_web/live/profile_live.ex:333 #, elixir-autogen, elixir-format msgid "You have not blocked anyone." msgstr "Ви нікого не заблокували." -#: lib/who_need_help_web/user_auth.ex:208 -#: lib/who_need_help_web/user_auth.ex:240 -#: lib/who_need_help_web/user_auth.ex:270 +#: lib/who_need_help_web/user_auth.ex:256 +#: lib/who_need_help_web/user_auth.ex:288 +#: lib/who_need_help_web/user_auth.ex:318 #, elixir-autogen, elixir-format msgid "You must log in to access this page." msgstr "Ви повинні увійти, щоб отримати доступ до цієї сторінки." -#: lib/who_need_help_web/user_auth.ex:178 +#: lib/who_need_help_web/user_auth.ex:226 #, elixir-autogen, elixir-format msgid "You must re-authenticate to access this page." msgstr "" @@ -2612,22 +2543,22 @@ msgstr "" "Щоб виконувати чутливі дії з обліковим записом, потрібно повторно " "автентифікуватися." -#: lib/who_need_help_web/live/request_live/show.ex:117 +#: lib/who_need_help_web/live/request_live/show.ex:142 #, elixir-autogen, elixir-format msgid "You withdrew from this request." msgstr "Ви відмовилися допомагати з цією заявкою." -#: lib/who_need_help_web/live/activity_live/index.ex:221 +#: lib/who_need_help_web/live/activity_live/index.ex:292 #, elixir-autogen, elixir-format msgid "Your activities" msgstr "Ваші активності" -#: lib/who_need_help_web/live/request_live/show.ex:113 +#: lib/who_need_help_web/live/request_live/show.ex:138 #, elixir-autogen, elixir-format msgid "Your confirmation was saved." msgstr "Ваше підтвердження збережено." -#: lib/who_need_help_web/live/profile_live.ex:139 +#: lib/who_need_help_web/live/profile_live.ex:142 #, elixir-autogen, elixir-format msgid "Your profile" msgstr "Ваш профіль" @@ -2637,53 +2568,53 @@ msgstr "Ваш профіль" msgid "Your request is now visible to nearby helpers." msgstr "Тепер вашу заявку бачать помічники поруч." -#: lib/who_need_help_web/live/request_live/index.ex:266 +#: lib/who_need_help_web/live/request_live/index.ex:342 #, elixir-autogen, elixir-format msgid "Your requests" msgstr "Ваші запити" -#: lib/who_need_help_web/live/request_live/show.ex:706 +#: lib/who_need_help_web/live/request_live/show.ex:817 #, elixir-autogen, elixir-format msgid "Your review is revealed only after both participants submit." msgstr "" "Ваш відгук відкриється лише після того, як його надішлють обидва учасники." -#: lib/who_need_help_web/live/request_live/index.ex:243 +#: lib/who_need_help_web/live/request_live/index.ex:319 #, elixir-autogen, elixir-format msgid "Your trust summary" msgstr "Ваші показники довіри" -#: lib/who_need_help_web/live/moderation_live.ex:307 +#: lib/who_need_help_web/live/moderation_live.ex:311 #, elixir-autogen, elixir-format msgid "activity" msgstr "активність" -#: lib/who_need_help_web/live/moderation_live.ex:310 +#: lib/who_need_help_web/live/moderation_live.ex:314 #, elixir-autogen, elixir-format msgid "activity message" msgstr "повідомлення активності" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 #, elixir-autogen, elixir-format msgid "before confirming." msgstr "перед підтвердженням." -#: lib/who_need_help_web/live/activity_live/index.ex:190 +#: lib/who_need_help_web/live/activity_live/index.ex:261 #: lib/who_need_help_web/live/category_proposal_live.ex:147 -#: lib/who_need_help_web/live/moderation_live.ex:313 -#: lib/who_need_help_web/live/request_live/index.ex:210 +#: lib/who_need_help_web/live/moderation_live.ex:317 +#: lib/who_need_help_web/live/request_live/index.ex:286 #, elixir-autogen, elixir-format msgid "by %{name}" msgstr "автор: %{name}" -#: lib/who_need_help_web/live/profile_live.ex:186 -#: lib/who_need_help_web/live/request_live/index.ex:247 -#: lib/who_need_help_web/live/request_live/show.ex:772 +#: lib/who_need_help_web/live/profile_live.ex:189 +#: lib/who_need_help_web/live/request_live/index.ex:323 +#: lib/who_need_help_web/live/request_live/show.ex:883 #, elixir-autogen, elixir-format msgid "completed" msgstr "завершено" -#: lib/who_need_help_web/live/request_live/index.ex:215 +#: lib/who_need_help_web/live/request_live/index.ex:291 #, elixir-autogen, elixir-format msgid "expires %{time}" msgstr "спливає %{time}" @@ -2693,59 +2624,59 @@ msgstr "спливає %{time}" msgid "for an account now." msgstr "обліковий запис зараз." -#: lib/who_need_help_web/live/request_live/show.ex:799 +#: lib/who_need_help_web/live/request_live/show.ex:910 #, elixir-autogen, elixir-format msgid "helper movement supported" msgstr "рух помічника підтверджено" -#: lib/who_need_help_web/live/request_live/show.ex:800 +#: lib/who_need_help_web/live/request_live/show.ex:911 #, elixir-autogen, elixir-format msgid "no movement evidence" msgstr "немає підтвердження руху" -#: lib/who_need_help_web/live/request_live/show.ex:791 +#: lib/who_need_help_web/live/request_live/show.ex:902 #, elixir-autogen, elixir-format msgid "no proximity evidence" msgstr "немає підтвердження близькості" -#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:49 +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82 #, elixir-autogen, elixir-format msgid "or" msgstr "або" -#: lib/who_need_help_web/live/activity_live/show.ex:586 +#: lib/who_need_help_web/live/activity_live/show.ex:605 #, elixir-autogen, elixir-format msgid "organizer" msgstr "організатор" -#: lib/who_need_help_web/live/request_live/show.ex:648 +#: lib/who_need_help_web/live/request_live/show.ex:756 #, elixir-autogen, elixir-format msgid "participants only" msgstr "лише учасники" -#: lib/who_need_help_web/live/request_live/index.ex:252 -#: lib/who_need_help_web/live/request_live/show.ex:776 +#: lib/who_need_help_web/live/request_live/index.ex:328 +#: lib/who_need_help_web/live/request_live/show.ex:887 #, elixir-autogen, elixir-format msgid "people" msgstr "люди" -#: lib/who_need_help_web/live/request_live/show.ex:790 +#: lib/who_need_help_web/live/request_live/show.ex:901 #, elixir-autogen, elixir-format msgid "proximity supported" msgstr "близькість підтверджено" -#: lib/who_need_help_web/live/profile_live.ex:201 -#: lib/who_need_help_web/live/request_live/show.ex:780 +#: lib/who_need_help_web/live/profile_live.ex:204 +#: lib/who_need_help_web/live/request_live/show.ex:891 #, elixir-autogen, elixir-format msgid "rating" msgstr "оцінка" -#: lib/who_need_help_web/live/request_live/show.ex:640 +#: lib/who_need_help_web/live/request_live/show.ex:748 #, elixir-autogen, elixir-format msgid "rating %{rating}" msgstr "оцінка %{rating}" -#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:42 +#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:41 #, elixir-autogen, elixir-format msgid "safety rules" msgstr "правила безпеки" @@ -2760,17 +2691,17 @@ msgstr "сторінка поштової скриньки" msgid "to your account now." msgstr "до свого облікового запису зараз." -#: lib/who_need_help_web/live/profile_live.ex:191 +#: lib/who_need_help_web/live/profile_live.ex:194 #, elixir-autogen, elixir-format msgid "unique people" msgstr "унікальні люди" -#: lib/who_need_help_web/live/activity_live/show.ex:395 -#: lib/who_need_help_web/live/activity_live/show.ex:546 -#: lib/who_need_help_web/live/activity_live/show.ex:605 -#: lib/who_need_help_web/live/profile_live.ex:274 -#: lib/who_need_help_web/live/request_live/show.ex:627 -#: lib/who_need_help_web/live/request_live/show.ex:821 +#: lib/who_need_help_web/live/activity_live/show.ex:414 +#: lib/who_need_help_web/live/activity_live/show.ex:565 +#: lib/who_need_help_web/live/activity_live/show.ex:624 +#: lib/who_need_help_web/live/profile_live.ex:277 +#: lib/who_need_help_web/live/request_live/show.ex:735 +#: lib/who_need_help_web/live/request_live/show.ex:932 #, elixir-autogen, elixir-format msgid "unverified" msgstr "не підтверджено" @@ -2780,34 +2711,34 @@ msgstr "не підтверджено" msgid "urgent" msgstr "терміново" -#: lib/who_need_help_web/live/activity_live/show.ex:394 -#: lib/who_need_help_web/live/activity_live/show.ex:545 -#: lib/who_need_help_web/live/activity_live/show.ex:604 -#: lib/who_need_help_web/live/profile_live.ex:273 -#: lib/who_need_help_web/live/request_live/index.ex:257 -#: lib/who_need_help_web/live/request_live/show.ex:626 -#: lib/who_need_help_web/live/request_live/show.ex:820 +#: lib/who_need_help_web/live/activity_live/show.ex:413 +#: lib/who_need_help_web/live/activity_live/show.ex:564 +#: lib/who_need_help_web/live/activity_live/show.ex:623 +#: lib/who_need_help_web/live/profile_live.ex:276 +#: lib/who_need_help_web/live/request_live/index.ex:333 +#: lib/who_need_help_web/live/request_live/show.ex:734 +#: lib/who_need_help_web/live/request_live/show.ex:931 #, elixir-autogen, elixir-format msgid "verified" msgstr "підтверджено" -#: lib/who_need_help_web/live/profile_live.ex:196 +#: lib/who_need_help_web/live/profile_live.ex:199 #, elixir-autogen, elixir-format msgid "verified handovers" msgstr "підтверджені передачі" -#: lib/who_need_help_web/live/request_live/show.ex:552 +#: lib/who_need_help_web/live/request_live/show.ex:660 #, elixir-autogen, elixir-format msgid "← All requests" msgstr "← Усі заявки" -#: lib/who_need_help_web/live/activity_live/new.ex:111 -#: lib/who_need_help_web/live/activity_live/show.ex:329 +#: lib/who_need_help_web/live/activity_live/new.ex:113 +#: lib/who_need_help_web/live/activity_live/show.ex:348 #, elixir-autogen, elixir-format msgid "← Back to activities" msgstr "← Назад до активностей" -#: lib/who_need_help_web/live/request_live/new.ex:150 +#: lib/who_need_help_web/live/request_live/new.ex:152 #, elixir-autogen, elixir-format msgid "← Back to requests" msgstr "← Назад до заявок" @@ -2817,83 +2748,221 @@ msgstr "← Назад до заявок" msgid "← Requests" msgstr "← Заявки" -#: lib/who_need_help_web/live/moderation_live.ex:258 +#: lib/who_need_help_web/live/moderation_live.ex:262 #, elixir-autogen, elixir-format msgid "Approved" msgstr "Схвалено" -#: lib/who_need_help_web/live/request_live/index.ex:112 -#: lib/who_need_help_web/live/request_live/index.ex:113 +#: lib/who_need_help_web/live/request_live/index.ex:186 +#: lib/who_need_help_web/live/request_live/index.ex:187 #, elixir-autogen, elixir-format msgid "Expired" msgstr "Спливло" -#: lib/who_need_help_web/live/moderation_live.ex:275 +#: lib/who_need_help_web/live/moderation_live.ex:279 #, elixir-autogen, elixir-format msgid "Handover without location evidence" msgstr "Передача без підтвердження геолокацією" -#: lib/who_need_help_web/live/moderation_live.ex:277 +#: lib/who_need_help_web/live/moderation_live.ex:281 #, elixir-autogen, elixir-format msgid "Location without movement" msgstr "Геолокація без руху" -#: lib/who_need_help_web/live/moderation_live.ex:279 +#: lib/who_need_help_web/live/moderation_live.ex:283 #, elixir-autogen, elixir-format msgid "Manual review" msgstr "Ручна перевірка" -#: lib/who_need_help_web/live/moderation_live.ex:260 +#: lib/who_need_help_web/live/moderation_live.ex:264 #, elixir-autogen, elixir-format msgid "Merged" msgstr "Об’єднано" -#: lib/who_need_help_web/live/moderation_live.ex:278 +#: lib/who_need_help_web/live/moderation_live.ex:282 #, elixir-autogen, elixir-format msgid "Reciprocal rating ring" msgstr "Кільце взаємних оцінок" -#: lib/who_need_help_web/live/moderation_live.ex:259 +#: lib/who_need_help_web/live/moderation_live.ex:263 #, elixir-autogen, elixir-format msgid "Rejected" msgstr "Відхилено" -#: lib/who_need_help_web/live/moderation_live.ex:272 +#: lib/who_need_help_web/live/moderation_live.ex:276 #, elixir-autogen, elixir-format msgid "Repeated participant pair" msgstr "Повторювана пара учасників" -#: lib/who_need_help_web/live/moderation_live.ex:271 +#: lib/who_need_help_web/live/moderation_live.ex:275 #, elixir-autogen, elixir-format msgid "Unusual action rate" msgstr "Незвична частота дій" -#: lib/who_need_help_web/live/activity_live/show.ex:429 -#: lib/who_need_help_web/live/request_live/show.ex:660 +#: lib/who_need_help_web/live/activity_live/show.ex:448 +#: lib/who_need_help_web/live/request_live/show.ex:768 #, elixir-autogen, elixir-format msgid "Load earlier messages" msgstr "Завантажити попередні повідомлення" -#: lib/who_need_help_web/live/activity_live/index.ex:202 -#: lib/who_need_help_web/live/activity_live/index.ex:238 +#: lib/who_need_help_web/live/activity_live/index.ex:273 +#: lib/who_need_help_web/live/activity_live/index.ex:310 #: lib/who_need_help_web/live/category_proposal_live.ex:162 #: lib/who_need_help_web/live/leaderboard_live.ex:87 -#: lib/who_need_help_web/live/moderation_live.ex:380 -#: lib/who_need_help_web/live/moderation_live.ex:415 -#: lib/who_need_help_web/live/moderation_live.ex:560 -#: lib/who_need_help_web/live/moderation_live.ex:652 -#: lib/who_need_help_web/live/profile_live.ex:221 -#: lib/who_need_help_web/live/profile_live.ex:351 -#: lib/who_need_help_web/live/request_live/index.ex:227 -#: lib/who_need_help_web/live/request_live/index.ex:283 +#: lib/who_need_help_web/live/moderation_live.ex:384 +#: lib/who_need_help_web/live/moderation_live.ex:419 +#: lib/who_need_help_web/live/moderation_live.ex:564 +#: lib/who_need_help_web/live/moderation_live.ex:656 +#: lib/who_need_help_web/live/profile_live.ex:224 +#: lib/who_need_help_web/live/profile_live.ex:354 +#: lib/who_need_help_web/live/request_live/index.ex:303 +#: lib/who_need_help_web/live/request_live/index.ex:360 #, elixir-autogen, elixir-format msgid "Load more" msgstr "Завантажити ще" -#: lib/who_need_help_web/live/activity_live/index.ex:213 -#: lib/who_need_help_web/live/activity_live/show.ex:479 -#: lib/who_need_help_web/live/request_live/index.ex:238 -#: lib/who_need_help_web/live/request_live/show.ex:738 +#: lib/who_need_help_web/live/activity_live/index.ex:284 +#: lib/who_need_help_web/live/activity_live/show.ex:498 +#: lib/who_need_help_web/live/request_live/index.ex:314 +#: lib/who_need_help_web/live/request_live/show.ex:849 #, elixir-autogen, elixir-format msgid "The map is unavailable in this browser. Request details remain usable." msgstr "Карта недоступна в цьому браузері. Решта інформації залишається доступною." + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:4 +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Confirm email change" +msgstr "Підтвердити зміну email" + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:5 +#, elixir-autogen, elixir-format +msgid "Confirm the new email address for your account." +msgstr "Підтвердьте новий email для свого облікового запису." + +#: lib/who_need_help_web/live/moderation_live.ex:242 +#, elixir-autogen, elixir-format +msgid "Could not complete moderation. Please try again." +msgstr "Не вдалося завершити модерацію. Спробуйте ще раз." + +#: lib/who_need_help_web/live/activity_live/show.ex:336 +#: lib/who_need_help_web/live/category_proposal_live.ex:60 +#: lib/who_need_help_web/live/request_live/show.ex:621 +#, elixir-autogen, elixir-format +msgid "Could not complete the action. Please try again." +msgstr "Не вдалося завершити дію. Спробуйте ще раз." + +#: lib/who_need_help_web/live/activity_live/new.ex:105 +#, elixir-autogen, elixir-format +msgid "Could not publish the activity. Please try again." +msgstr "Не вдалося опублікувати активність. Спробуйте ще раз." + +#: lib/who_need_help_web/live/request_live/new.ex:144 +#, elixir-autogen, elixir-format +msgid "Could not publish the request. Please try again." +msgstr "Не вдалося опублікувати заявку. Спробуйте ще раз." + +#: lib/who_need_help_web/live/profile_live.ex:91 +#, elixir-autogen, elixir-format +msgid "Could not unblock this user. Please try again." +msgstr "Не вдалося розблокувати користувача. Спробуйте ще раз." + +#: lib/who_need_help_web/controllers/user_settings_html/confirm_email.html.heex:22 +#, elixir-autogen, elixir-format +msgid "JavaScript is required to confirm this protected email link." +msgstr "Для підтвердження цього захищеного посилання на email потрібен JavaScript." + +#: lib/who_need_help_web/live/request_live/show.ex:594 +#, elixir-autogen, elixir-format +msgid "Last update: %{time}" +msgstr "Останнє оновлення: %{time}" + +#: lib/who_need_help_web/live/request_live/show.ex:617 +#, elixir-autogen, elixir-format +msgid "Live location sharing is not active." +msgstr "Передавання геопозиції зараз не активне." + +#: lib/who_need_help_web/live/request_live/show.ex:592 +#, elixir-autogen, elixir-format +msgid "Shared location" +msgstr "Передавана геопозиція" + +#: lib/who_need_help_web/live/activity_live/show.ex:329 +#, elixir-autogen, elixir-format +msgid "The organizer cannot leave the activity." +msgstr "Організатор не може залишити активність." + +#: lib/who_need_help_web/live/activity_live/show.ex:326 +#, elixir-autogen, elixir-format +msgid "The organizer is already attending." +msgstr "Організатор уже бере участь." + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "The registration form is invalid." +msgstr "Форму реєстрації заповнено неправильно." + +#: lib/who_need_help_web/live/moderation_live.ex:239 +#, elixir-autogen, elixir-format +msgid "The selected record is no longer available." +msgstr "Вибраний запис більше недоступний." + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "The settings form is invalid." +msgstr "Форму налаштувань заповнено неправильно." + +#: lib/who_need_help_web/controllers/user_session_controller.ex:111 +#, elixir-autogen, elixir-format +msgid "The sign-in form is invalid." +msgstr "Форму входу заповнено неправильно." + +#: lib/who_need_help_web/live/activity_live/show.ex:323 +#, elixir-autogen, elixir-format +msgid "This activity is no longer available." +msgstr "Ця активність більше недоступна." + +#: lib/who_need_help_web/live/request_live/show.ex:616 +#, elixir-autogen, elixir-format +msgid "This match is no longer active." +msgstr "Це зіставлення більше не активне." + +#: lib/who_need_help_web/live/category_proposal_live.ex:57 +#, elixir-autogen, elixir-format +msgid "This proposal is no longer available." +msgstr "Ця пропозиція більше недоступна." + +#: lib/who_need_help_web/live/request_live/show.ex:614 +#, elixir-autogen, elixir-format +msgid "This request has expired." +msgstr "Термін дії цієї заявки минув." + +#: lib/who_need_help_web/controllers/user_session_controller.ex:60 +#, elixir-autogen, elixir-format +msgid "Too many sign-in attempts in the configured time window." +msgstr "Забагато спроб входу протягом налаштованого проміжку часу." + +#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:46 +#, elixir-autogen, elixir-format +msgid "Your secure email link is ready. Continue to sign in." +msgstr "Захищене посилання з листа готове. Продовжте вхід." + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:128 +#, elixir-autogen, elixir-format +msgid "The confirmation email could not be sent. Please try again." +msgstr "Не вдалося надіслати лист із підтвердженням. Спробуйте ще раз." + +#: lib/who_need_help_web/controllers/user_registration_controller.ex:109 +#, elixir-autogen, elixir-format +msgid "If this address can be registered or signed in, login instructions will arrive shortly." +msgstr "Якщо цю адресу можна зареєструвати або використати для входу, інструкції для входу незабаром надійдуть." + +#: lib/who_need_help_web/live/profile_live.ex:62 +#, elixir-autogen, elixir-format +msgid "This account cannot perform that action." +msgstr "" + +#: lib/who_need_help_web/controllers/user_settings_controller.ex:40 +#, elixir-autogen, elixir-format +msgid "Too many email-change requests in the configured time window." +msgstr "Забагато запитів на зміну email протягом налаштованого проміжку часу." diff --git a/priv/gettext/uk/LC_MESSAGES/errors.po b/priv/gettext/uk/LC_MESSAGES/errors.po index 5bc5db3..51e9aa5 100644 --- a/priv/gettext/uk/LC_MESSAGES/errors.po +++ b/priv/gettext/uk/LC_MESSAGES/errors.po @@ -115,12 +115,12 @@ msgstr "має дорівнювати %{number}" msgid "contains an invalid field definition" msgstr "містить неправильне визначення поля" -#: lib/who_need_help_web/validation_messages.ex:22 +#: lib/who_need_help_web/validation_messages.ex:23 #, elixir-autogen, elixir-format msgid "did not change" msgstr "не змінилося" -#: lib/who_need_help_web/validation_messages.ex:23 +#: lib/who_need_help_web/validation_messages.ex:24 #, elixir-autogen, elixir-format msgid "does not match password" msgstr "не збігається з паролем" @@ -130,16 +130,11 @@ msgstr "не збігається з паролем" msgid "field keys must be unique" msgstr "ключі полів мають бути унікальними" -#: lib/who_need_help_web/validation_messages.ex:19 +#: lib/who_need_help_web/validation_messages.ex:20 #, elixir-autogen, elixir-format msgid "must be a GitHub profile URL" msgstr "має бути URL-адресою профілю GitHub" -#: lib/who_need_help_web/validation_messages.ex:18 -#, elixir-autogen, elixir-format -msgid "must be a complete http(s) URL" -msgstr "має бути повною URL-адресою з http(s)" - #: lib/who_need_help_web/validation_messages.ex:12 #, elixir-autogen, elixir-format msgid "must be an object with a fields array" @@ -165,7 +160,7 @@ msgstr "має містити щонайменше одну непорожню msgid "must contain non-empty localized descriptions" msgstr "має містити непорожні локалізовані описи" -#: lib/who_need_help_web/validation_messages.ex:21 +#: lib/who_need_help_web/validation_messages.ex:22 #, elixir-autogen, elixir-format msgid "must have the @ sign and no spaces" msgstr "має містити знак @ і не містити пробілів" @@ -185,7 +180,17 @@ msgstr "виберіть категорію активності" msgid "select exactly one report target" msgstr "виберіть рівно один об’єкт скарги" -#: lib/who_need_help_web/validation_messages.ex:20 +#: lib/who_need_help_web/validation_messages.ex:21 #, elixir-autogen, elixir-format msgid "you must confirm that you are 18+ and accept the rules" msgstr "підтвердьте, що вам виповнилося 18 років і ви приймаєте правила" + +#: lib/who_need_help_web/validation_messages.ex:18 +#, elixir-autogen, elixir-format +msgid "must be a complete HTTPS URL" +msgstr "має бути повною HTTPS URL-адресою" + +#: lib/who_need_help_web/validation_messages.ex:19 +#, elixir-autogen, elixir-format +msgid "must be a full HTTPS URL" +msgstr "має бути повною HTTPS URL-адресою" diff --git a/scripts/android-instrumentation-test.sh b/scripts/android-instrumentation-test.sh index 6863766..7c4b5fc 100755 --- a/scripts/android-instrumentation-test.sh +++ b/scripts/android-instrumentation-test.sh @@ -6,8 +6,9 @@ ANDROID_ENV="$ROOT/.env" TEST_ENV="$ROOT/.env.android-test" run_id=$(date -u +%Y%m%d%H%M%S)-$$ android_api=${WNH_ANDROID_TEST_API:-37.0} -# 1G is measured against the complete API 30/34/37 suite. It can still be -# overridden for a future test that intentionally stores more device data. +# 1G is measured against the API 30/34/37 suite and is also exercised by the +# API 24 minimum-SDK probe. It can still be overridden for a future test that +# intentionally stores more device data. android_data_partition_size=${WNH_ANDROID_TEST_DATA_PARTITION_SIZE:-1G} if ! printf '%s\n' "$android_data_partition_size" | @@ -17,6 +18,9 @@ if ! printf '%s\n' "$android_data_partition_size" | fi case "$android_api" in + 24) + android_system_image="system-images/android-24/google_apis/x86_64" + ;; 30) android_system_image="system-images/android-30/google_apis/x86_64" ;; @@ -27,7 +31,7 @@ case "$android_api" in android_system_image="system-images/android-37.0/google_apis_ps16k/x86_64" ;; *) - echo "WNH_ANDROID_TEST_API must be one of: 30, 34, 37.0." >&2 + echo "WNH_ANDROID_TEST_API must be one of: 24, 30, 34, 37.0." >&2 exit 1 ;; esac @@ -100,6 +104,12 @@ cleanup() { docker exec "$container" adb logcat -d > "$output/logcat.txt" 2>&1 || true docker exec "$container" adb shell dumpsys activity services \ org.whoneedhelp.mobile.debug > "$output/services.txt" 2>&1 || true + docker exec "$container" adb shell dumpsys notification --noredact \ + > "$output/notifications.txt" 2>&1 || true + docker exec "$container" adb shell uiautomator dump /sdcard/window.xml \ + > "$output/window-dump.txt" 2>&1 || true + docker exec "$container" adb shell cat /sdcard/window.xml \ + > "$output/window.xml" 2>&1 || true docker logs "$container" > "$output/emulator.log" 2>&1 || true fi @@ -151,7 +161,11 @@ docker exec "$container" adb shell input keyevent 82 docker exec "$container" adb shell settings put global window_animation_scale 0 docker exec "$container" adb shell settings put global transition_animation_scale 0 docker exec "$container" adb shell settings put global animator_duration_scale 0 -docker exec "$container" adb shell cmd location set-location-enabled true +if [ "$android_api" = "24" ]; then + docker exec "$container" adb shell settings put secure location_mode 3 +else + docker exec "$container" adb shell cmd location set-location-enabled true +fi docker exec "$container" adb emu geo fix -122.084000 37.422000 docker exec "$container" adb install -r \ /opt/who-need-help/who-need-help-debug.apk diff --git a/scripts/android-matrix-test.sh b/scripts/android-matrix-test.sh index fc1aa9b..05f3418 100755 --- a/scripts/android-matrix-test.sh +++ b/scripts/android-matrix-test.sh @@ -2,11 +2,11 @@ set -eu ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) -matrix=${WNH_ANDROID_TEST_API_MATRIX:-"30 34 37.0"} +matrix=${WNH_ANDROID_TEST_API_MATRIX:-"24 30 34 37.0"} for api in $matrix; do case "$api" in - 30|34|37.0) ;; + 24|30|34|37.0) ;; *) echo "WNH_ANDROID_TEST_API_MATRIX contains unsupported API: $api" >&2 exit 1 diff --git a/scripts/load-resilience-run.sh b/scripts/load-resilience-run.sh index 99925f1..f500477 100755 --- a/scripts/load-resilience-run.sh +++ b/scripts/load-resilience-run.sh @@ -19,7 +19,7 @@ for name in LOAD_PROJECT LOAD_HOST LOAD_WEB_REPLICAS LOAD_WORKER_REPLICAS \ LOAD_RESILIENCE_RECOVERY_TIMEOUT_SECONDS \ LOAD_RESILIENCE_PROBE_INTERVAL_SECONDS \ LOAD_RESILIENCE_REQUEST_TIMEOUT_SECONDS TRAEFIK_RETRY_ATTEMPTS \ - HTTP_PORT POSTGRES_DB; do + HTTP_PORT POSTGRES_DB METRICS_TOKEN; do if [[ -z "${!name:-}" ]]; then echo "$name is missing from .env.load" >&2 exit 1 @@ -184,6 +184,7 @@ sample_readiness() { observed_at=$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ) body_file="$output_dir/readiness-body.$$" error_file="$output_dir/readiness-error.$$" + metrics_headers_file="$output_dir/metrics-headers.$$" set +e status=$( curl --silent --show-error \ @@ -206,15 +207,55 @@ sample_readiness() { body= fi + set +e + metrics_status=$( + curl --silent --show-error \ + --max-time "$LOAD_RESILIENCE_REQUEST_TIMEOUT_SECONDS" \ + --header "Host: $LOAD_HOST" \ + --header "Authorization: Bearer $METRICS_TOKEN" \ + --dump-header "$metrics_headers_file" \ + --output /dev/null \ + --write-out '%{http_code}' \ + "http://localhost:$HTTP_PORT/metrics" 2>>"$error_file" + ) + metrics_curl_status=$? + set -e + + if [[ "$metrics_curl_status" -ne 0 ]]; then + metrics_status=000 + fi + + node=$( + awk ' + BEGIN {IGNORECASE = 1} + /^x-wnh-node:/ { + sub(/^[^:]+:[[:space:]]*/, "") + sub(/\r$/, "") + print + exit + } + ' "$metrics_headers_file" 2>/dev/null || true + ) + error=$(tr -d '\n' <"$error_file") unlink "$body_file" 2>/dev/null || true unlink "$error_file" 2>/dev/null || true + unlink "$metrics_headers_file" 2>/dev/null || true jq -cn \ --arg observed_at "$observed_at" \ --arg status "$status" \ + --arg metrics_status "$metrics_status" \ + --arg node "$node" \ --arg body "$body" \ --arg error "$error" \ - '{observed_at: $observed_at, status: $status, body: $body, error: $error}' \ + '{ + observed_at: $observed_at, + status: $status, + metrics_status: $metrics_status, + node: $node, + body: $body, + error: $error + }' \ >>"$probe_log" sleep "$LOAD_RESILIENCE_PROBE_INTERVAL_SECONDS" done @@ -338,7 +379,7 @@ mapfile -t original_web_ids < <(service_ids web) for container_id in "${original_web_ids[@]}"; do assert_scope "$container_id" web { - docker stop --time 30 "$container_id" + docker stop --timeout 30 "$container_id" docker rm "$container_id" "${compose[@]}" up -d --no-deps --scale "web=$LOAD_WEB_REPLICAS" web } >>"$output_dir/web-replacements.txt" @@ -350,7 +391,7 @@ mapfile -t original_worker_ids < <(service_ids worker) for container_id in "${original_worker_ids[@]}"; do assert_scope "$container_id" worker { - docker stop --time 30 "$container_id" + docker stop --timeout 30 "$container_id" docker rm "$container_id" "${compose[@]}" up -d --no-deps --scale "worker=$LOAD_WORKER_REPLICAS" worker } >>"$output_dir/worker-replacements.txt" @@ -438,8 +479,8 @@ probe_pid= jq -s '{ samples: length, - failures: (map(select(.status != "200")) | length), - nodes: (map(.body | fromjson? | .node) | map(select(. != null)) | unique) + failures: (map(select(.status != "200" or .metrics_status != "200")) | length), + nodes: (map(.node) | map(select(. != null and . != "")) | unique) }' "$probe_log" >"$output_dir/readiness-summary.json" if ! jq -e '.samples > 0 and .failures == 0 and (.nodes | length) >= 2' \ diff --git a/scripts/quality.sh b/scripts/quality.sh index 0cd216c..ef74058 100755 --- a/scripts/quality.sh +++ b/scripts/quality.sh @@ -213,7 +213,16 @@ echo "Linting the Helm chart" "$ROOT/deploy/helm/who-need-help" echo "Scanning only tracked and non-ignored source files" -git ls-files --cached --others --exclude-standard -z >"$scan_list" +# The single-quoted program must expand $path inside the child shell. +# shellcheck disable=SC2016 +git ls-files --cached --others --exclude-standard -z | + xargs -0 -r sh -c ' + for path do + if [ -f "$path" ]; then + printf "%s\0" "$path" + fi + done + ' sh >"$scan_list" tar --null --no-recursion --files-from="$scan_list" --create --file="$scan_tar" tar --extract --file="$scan_tar" --directory "$scan_dir" "$ROOT/.tools/bin/helm" template who-need-help \ diff --git a/test/who_need_help/accounts_test.exs b/test/who_need_help/accounts_test.exs index 29a372c..3c7a7d8 100644 --- a/test/who_need_help/accounts_test.exs +++ b/test/who_need_help/accounts_test.exs @@ -4,7 +4,7 @@ defmodule WhoNeedHelp.AccountsTest do alias WhoNeedHelp.Accounts import WhoNeedHelp.AccountsFixtures - alias WhoNeedHelp.Accounts.{User, UserToken} + alias WhoNeedHelp.Accounts.{SocialIdentity, User, UserToken} describe "get_user_by_email/1" do test "does not return the user if the email does not exist" do @@ -14,6 +14,7 @@ defmodule WhoNeedHelp.AccountsTest do test "returns the user if the email exists" do %{id: id} = user = user_fixture() assert %User{id: ^id} = Accounts.get_user_by_email(user.email) + assert %User{id: ^id} = Accounts.get_user_by_email(" #{String.upcase(user.email)} ") end end @@ -32,6 +33,12 @@ defmodule WhoNeedHelp.AccountsTest do assert %User{id: ^id} = Accounts.get_user_by_email_and_password(user.email, valid_user_password()) + + assert %User{id: ^id} = + Accounts.get_user_by_email_and_password( + " #{String.upcase(user.email)} ", + valid_user_password() + ) end test "does not authenticate a suspended user with a valid password" do @@ -140,6 +147,67 @@ defmodule WhoNeedHelp.AccountsTest do end end + describe "profile URL validation" do + test "accepts only encrypted thank-you and social profile URLs" do + user = user_fixture() + + profile_attrs = %{ + "display_name" => user.display_name, + "locale" => user.locale, + "location_visibility" => user.location_visibility, + "direct_message_policy" => user.direct_message_policy + } + + assert User.profile_changeset( + user, + Map.put(profile_attrs, "tip_url", "https://example.com/thank-you") + ).valid? + + refute User.profile_changeset( + user, + Map.put(profile_attrs, "tip_url", "http://example.com/thank-you") + ).valid? + + social = %SocialIdentity{user_id: Ecto.UUID.generate()} + social_attrs = %{"provider" => "other", "profile_url" => "https://example.com/profile"} + + assert SocialIdentity.changeset(social, social_attrs).valid? + + refute SocialIdentity.changeset( + social, + Map.put(social_attrs, "profile_url", "http://example.com/profile") + ).valid? + end + + test "rejects a thank-you URL that cannot fit its database column" do + user = user_fixture() + + changeset = + User.profile_changeset(user, %{ + "display_name" => user.display_name, + "locale" => user.locale, + "location_visibility" => user.location_visibility, + "direct_message_policy" => user.direct_message_policy, + "tip_url" => "https://example.com/#{String.duplicate("x", 240)}" + }) + + assert "should be at most 255 character(s)" in errors_on(changeset).tip_url + end + + test "rejects a social URL that cannot fit its database column" do + changeset = + SocialIdentity.changeset( + %SocialIdentity{user_id: Ecto.UUID.generate()}, + %{ + "provider" => "other", + "profile_url" => "https://example.com/#{String.duplicate("x", 240)}" + } + ) + + assert "should be at most 255 character(s)" in errors_on(changeset).profile_url + end + end + describe "change_user_email/3" do test "returns a user changeset" do assert %Ecto.Changeset{} = changeset = Accounts.change_user_email(%User{}) @@ -364,6 +432,10 @@ defmodule WhoNeedHelp.AccountsTest do end describe "login_user_by_magic_link/1" do + test "returns not found for a syntactically invalid token" do + assert {:error, :not_found} = Accounts.login_user_by_magic_link("%not-base64%") + end + test "confirms user and expires tokens" do user = unconfirmed_user_fixture() refute user.confirmed_at @@ -384,6 +456,24 @@ defmodule WhoNeedHelp.AccountsTest do assert {:error, :not_found} = Accounts.login_user_by_magic_link(encoded_token) end + test "concurrent requests consume an unconfirmed user's token exactly once" do + user = unconfirmed_user_fixture() + {encoded_token, _hashed_token} = generate_user_magic_link_token(user) + + results = + 1..2 + |> Task.async_stream( + fn _ -> Accounts.login_user_by_magic_link(encoded_token) end, + max_concurrency: 2, + ordered: false + ) + |> Enum.map(fn {:ok, result} -> result end) + + assert Enum.count(results, &match?({:ok, {%User{}, _tokens}}, &1)) == 1 + assert Enum.count(results, &(&1 == {:error, :not_found})) == 1 + assert Accounts.get_user!(user.id).confirmed_at + end + test "rejects a suspended user and consumes the magic link" do user = user_fixture() @@ -419,6 +509,54 @@ defmodule WhoNeedHelp.AccountsTest do end end + describe "delete_expired_user_tokens/1" do + test "prunes each token context only after its configured validity" do + user = user_fixture() + now = DateTime.utc_now(:second) + + expired_login = + Repo.insert!(%UserToken{ + user_id: user.id, + token: :crypto.strong_rand_bytes(32), + context: "login", + sent_to: user.email, + inserted_at: DateTime.add(now, -16, :minute) + }) + + active_login = + Repo.insert!(%UserToken{ + user_id: user.id, + token: :crypto.strong_rand_bytes(32), + context: "login", + sent_to: user.email, + inserted_at: DateTime.add(now, -14, :minute) + }) + + expired_change = + Repo.insert!(%UserToken{ + user_id: user.id, + token: :crypto.strong_rand_bytes(32), + context: "change:#{user.email}", + sent_to: unique_user_email(), + inserted_at: DateTime.add(now, -8, :day) + }) + + expired_session = + Repo.insert!(%UserToken{ + user_id: user.id, + token: :crypto.strong_rand_bytes(32), + context: "session", + inserted_at: DateTime.add(now, -15, :day) + }) + + assert {3, _} = Accounts.delete_expired_user_tokens(now) + refute Repo.get(UserToken, expired_login.id) + refute Repo.get(UserToken, expired_change.id) + refute Repo.get(UserToken, expired_session.id) + assert Repo.get(UserToken, active_login.id) + end + end + describe "get_user_by_session_token/1 moderation boundary" do test "never authenticates a suspended user even if a token still exists" do user = user_fixture() diff --git a/test/who_need_help/activities_test.exs b/test/who_need_help/activities_test.exs index 56cf9d3..8efd9cb 100644 --- a/test/who_need_help/activities_test.exs +++ b/test/who_need_help/activities_test.exs @@ -88,12 +88,113 @@ defmodule WhoNeedHelp.ActivitiesTest do Activities.request_to_join(context.outsider_scope, activity.id) end - test "invalid participant identifiers are rejected", context do + test "global activity events carry only the bounded index projection", context do + :ok = Activities.subscribe() + {:ok, activity} = Activities.create_activity(context.organizer_scope, context.attrs) + + assert_receive {:activity_created, created} + assert created.id == activity.id + assert created.approved_participant_count == 1 + assert %Ecto.Association.NotLoaded{} = created.participants + assert %Ecto.Association.NotLoaded{} = created.messages + assert %Ecto.Association.NotLoaded{} = created.creator.social_identities + + :ok = Activities.subscribe_user(context.participant_scope) + {:ok, _request} = Activities.request_to_join(context.participant_scope, activity.id) + + assert_receive {:activity_updated, updated} + assert updated.id == activity.id + assert updated.approved_participant_count == 1 + assert %Ecto.Association.NotLoaded{} = updated.participants + assert %Ecto.Association.NotLoaded{} = updated.messages + assert_receive {:my_activity_updated, participant_update, true} + assert participant_update.id == activity.id + end + + test "invalid and missing client identifiers are rejected without raising", context do assert {:error, :not_found} = Activities.approve_participant(context.organizer_scope, "not-a-uuid") assert {:error, :not_found} = Activities.decline_participant(context.organizer_scope, "not-a-uuid") + + missing_id = Ecto.UUID.generate() + + assert {:error, :not_found} = + Activities.approve_participant(context.organizer_scope, missing_id) + + assert {:error, :not_found} = + Activities.decline_participant(context.organizer_scope, missing_id) + + assert {:error, :not_found} = + Activities.request_to_join(context.participant_scope, missing_id) + + assert {:error, :not_found} = + Activities.send_message(context.participant_scope, missing_id, %{"body" => "No"}) + + assert {:error, :not_found} = + Activities.cancel_activity(context.organizer_scope, missing_id) + + assert {:error, :not_found} = + Activities.complete_activity(context.organizer_scope, missing_id) + + for invalid_id <- ["not-a-uuid", missing_id] do + assert {:error, :not_found} = + Activities.leave_activity(context.participant_scope, invalid_id) + + assert {:error, :not_found} = + Activities.request_to_join(context.participant_scope, invalid_id) + + assert {:error, :not_found} = + Activities.send_message(context.participant_scope, invalid_id, %{"body" => "No"}) + + assert {:error, :not_found} = + Activities.cancel_activity(context.organizer_scope, invalid_id) + + assert {:error, :not_found} = + Activities.complete_activity(context.organizer_scope, invalid_id) + end + end + + test "activity location labels are validated before the database write", context do + assert {:error, changeset} = + Activities.create_activity( + context.organizer_scope, + Map.put(context.attrs, "location_label", String.duplicate("x", 256)) + ) + + assert "should be at most 255 character(s)" in errors_on(changeset).location_label + end + + test "activity capacity outside the PostgreSQL integer range is rejected before insert", + context do + assert {:error, changeset} = + Activities.create_activity( + context.organizer_scope, + Map.put(context.attrs, "capacity", 2_147_483_648) + ) + + assert "must be less than or equal to 2147483647" in errors_on(changeset).capacity + end + + test "forged coordinate types become validation errors instead of raising", context do + assert {:error, changeset} = + Activities.create_activity( + context.organizer_scope, + Map.put(context.attrs, "latitude", %{"nested" => "value"}) + ) + + assert "select a valid location" in errors_on(changeset).location + end + + test "forged category filters return no activities instead of raising", context do + assert {:ok, _activity} = + Activities.create_activity(context.organizer_scope, context.attrs) + + assert [] == + Activities.list_open_activities(context.participant_scope, %{ + "category_id" => "not-a-uuid" + }) end test "unapproved viewers receive no private chat or pending participant data", context do diff --git a/test/who_need_help/mutual_aid_flow_test.exs b/test/who_need_help/mutual_aid_flow_test.exs index 48a97fc..dbde6c2 100644 --- a/test/who_need_help/mutual_aid_flow_test.exs +++ b/test/who_need_help/mutual_aid_flow_test.exs @@ -79,6 +79,61 @@ defmodule WhoNeedHelp.MutualAidFlowTest do assert {:error, :not_found} = Catalog.unvote(context.requester_scope, "not-a-uuid") end + test "invalid request workflow identifiers and handover codes are rejected", context do + missing_id = Ecto.UUID.generate() + + for id <- ["not-a-uuid", missing_id] do + assert {:error, :not_found} = Help.accept_request(context.helper_scope, id) + assert {:error, :not_found} = Help.cancel_request(context.requester_scope, id) + assert {:error, :not_found} = Help.start_assignment(context.helper_scope, id) + assert {:error, :not_found} = Help.confirm_completion(context.helper_scope, id) + assert {:error, :not_found} = Help.withdraw_assignment(context.helper_scope, id) + + assert {:error, :not_found} = + Help.verify_handover(context.helper_scope, id, "123456") + end + + {:ok, request} = Help.create_request(context.requester_scope, context.request_attrs) + {:ok, assignment} = Help.accept_request(context.helper_scope, request.id) + + for code <- [%{"nested" => "123456"}, "12345", "1234567", "abcdef"] do + assert {:error, :invalid_code} = + Help.verify_handover(context.helper_scope, assignment.id, code) + end + end + + test "request location labels are validated before the database write", context do + assert {:error, changeset} = + Help.create_request( + context.requester_scope, + Map.put(context.request_attrs, "location_label", String.duplicate("x", 256)) + ) + + assert "should be at most 255 character(s)" in errors_on(changeset).location_label + end + + test "forged request coordinate types become validation errors instead of raising", context do + assert {:error, changeset} = + Help.create_request( + context.requester_scope, + Map.put(context.request_attrs, "longitude", %{"nested" => "value"}) + ) + + assert "select a valid location" in errors_on(changeset).location + end + + test "forged filters return no requests instead of raising", context do + assert [] == + Help.list_open_requests(context.helper_scope, %{ + "category_id" => "not-a-uuid" + }) + + assert [] == + Help.list_open_requests(context.helper_scope, %{ + "urgency" => "impossible" + }) + end + test "chat is durable and only visible to match participants", context do outsider_scope = user_fixture(display_name: "Outsider") |> user_scope_fixture() {:ok, request} = Help.create_request(context.requester_scope, context.request_attrs) @@ -211,6 +266,21 @@ defmodule WhoNeedHelp.MutualAidFlowTest do assert oldest.next_cursor == nil end + test "chat and review return not found for a stale assignment reference", context do + stale_assignment = %WhoNeedHelp.Help.Assignment{id: Ecto.UUID.generate()} + + assert {:error, :not_found} = + Messaging.send_message(context.helper_scope, stale_assignment, %{ + "body" => "This assignment no longer exists" + }) + + assert {:error, :not_found} = + Trust.submit_review(context.helper_scope, stale_assignment, %{ + "rating" => 5, + "comment" => "This assignment no longer exists" + }) + end + test "stopping tracking deletes the exact current position", context do {:ok, request} = Help.create_request(context.requester_scope, context.request_attrs) {:ok, assignment} = Help.accept_request(context.helper_scope, request.id) @@ -229,6 +299,21 @@ defmodule WhoNeedHelp.MutualAidFlowTest do refute Repo.get(Position, position.id) end + test "forged tracking coordinate types become changeset errors instead of raising", context do + {:ok, request} = Help.create_request(context.requester_scope, context.request_attrs) + {:ok, assignment} = Help.accept_request(context.helper_scope, request.id) + {:ok, _session} = Tracking.start_session(context.helper_scope, assignment) + + assert {:error, changeset} = + Tracking.update_position(context.helper_scope, assignment, %{ + "latitude" => %{"nested" => "value"}, + "longitude" => 30.52, + "accuracy_meters" => 12.0 + }) + + assert "is invalid" in errors_on(changeset).position + end + test "concurrent tracking stops serialize and broadcast once", context do {:ok, request} = Help.create_request(context.requester_scope, context.request_attrs) {:ok, assignment} = Help.accept_request(context.helper_scope, request.id) diff --git a/test/who_need_help/trust_safety_test.exs b/test/who_need_help/trust_safety_test.exs index e2fa8ac..f8e3c9e 100644 --- a/test/who_need_help/trust_safety_test.exs +++ b/test/who_need_help/trust_safety_test.exs @@ -62,6 +62,17 @@ defmodule WhoNeedHelp.TrustSafetyTest do assert {:error, :not_found} = Trust.unblock(context.requester_scope, "not-a-uuid") end + test "invalid report target identifiers are rejected without a query cast failure", context do + for target <- ~w(request_id assignment_id message_id activity_id activity_message_id) do + assert {:error, :not_found} = + Trust.report(context.requester_scope, %{ + target => "not-a-uuid", + "reason" => "unsafe", + "details" => "Malformed target identifiers must not reach Ecto queries." + }) + end + end + test "forged moderation identifiers return not found without crashing", context do moderator = user_fixture(display_name: "Moderator") @@ -157,6 +168,8 @@ defmodule WhoNeedHelp.TrustSafetyTest do assert entry.unique_people == 1 assert entry.verified_people == 1 assert entry.location_supported_people == 1 + assert is_nil(entry.user.email) + assert is_nil(entry.user.hashed_password) assert Repo.exists?( from signal in AbuseSignal, @@ -240,6 +253,14 @@ defmodule WhoNeedHelp.TrustSafetyTest do assert {:error, changeset} = Help.create_request(context.requester_scope, invalid) assert "pickup_status has an invalid value" in errors_on(changeset).structured_data + + nested = + Map.put(context.attrs, "structured_data", %{ + "pickup_status" => %{"unexpected" => "reserved"} + }) + + assert {:error, changeset} = Help.create_request(context.requester_scope, nested) + assert "pickup_status has an invalid value" in errors_on(changeset).structured_data end test "help creation rejects missing safety consent and non-help categories", context do @@ -284,6 +305,15 @@ defmodule WhoNeedHelp.TrustSafetyTest do ) end + test "malformed runtime rate-limit configuration never crashes a request", context do + old = Application.get_env(:who_need_help, :rate_limit_policies) + Application.put_env(:who_need_help, :rate_limit_policies, ["not", "a", "map"]) + on_exit(fn -> Application.put_env(:who_need_help, :rate_limit_policies, old) end) + + assert {:ok, :not_configured} = + RateLimiter.check(:test_action, context.requester.id) + end + test "restricted accounts cannot perform trust-sensitive actions", context do context.helper |> WhoNeedHelp.Accounts.User.moderation_changeset(%{moderation_status: :restricted}) @@ -293,14 +323,16 @@ defmodule WhoNeedHelp.TrustSafetyTest do Help.create_request(context.helper_scope, context.attrs) end - test "hidden request coordinates are absent publicly but exact for a participant", context do + test "hidden request coordinates remain hidden from an accepted helper", context do hidden_attrs = Map.put(context.attrs, "location_visibility", "hidden") {:ok, request} = Help.create_request(context.requester_scope, hidden_attrs) + {:ok, _assignment} = Help.accept_request(context.helper_scope, request.id) request = Help.get_request!(request.id) outsider_scope = user_fixture(display_name: "Outsider") |> user_scope_fixture() assert is_nil(WhoNeedHelp.Help.HelpRequest.public_coordinates(request)) assert is_nil(Help.request_coordinates(outsider_scope, request)) + assert is_nil(Help.request_coordinates(context.helper_scope, request)) assert %{latitude: 50.4501, longitude: 30.5234, exact: true} = Help.request_coordinates(context.requester_scope, request) diff --git a/test/who_need_help_web/controllers/metrics_controller_test.exs b/test/who_need_help_web/controllers/metrics_controller_test.exs index 8d29d1a..93cc5fd 100644 --- a/test/who_need_help_web/controllers/metrics_controller_test.exs +++ b/test/who_need_help_web/controllers/metrics_controller_test.exs @@ -34,6 +34,7 @@ defmodule WhoNeedHelpWeb.MetricsControllerTest do ] assert get_resp_header(conn, "cache-control") == ["no-store"] + assert get_resp_header(conn, "x-wnh-node") == [to_string(node())] assert body =~ "# TYPE who_need_help_http_requests_total counter" assert body =~ "who_need_help_http_requests_total " assert body =~ "who_need_help_http_request_duration_microseconds_total " diff --git a/test/who_need_help_web/controllers/page_controller_test.exs b/test/who_need_help_web/controllers/page_controller_test.exs index 34d478b..4a2cbb2 100644 --- a/test/who_need_help_web/controllers/page_controller_test.exs +++ b/test/who_need_help_web/controllers/page_controller_test.exs @@ -14,6 +14,10 @@ defmodule WhoNeedHelpWeb.PageControllerTest do assert content_security_policy =~ "frame-ancestors 'none'" assert content_security_policy =~ "https://tile.openstreetmap.org" + assert get_resp_header(conn, "permissions-policy") == [ + "geolocation=(self), camera=(), microphone=(), payment=(), usb=()" + ] + assert html =~ ~s(data-map-tile-url="https://tile.openstreetmap.org/{z}/{x}/{y}.png") @@ -34,6 +38,22 @@ defmodule WhoNeedHelpWeb.PageControllerTest do ) == 1 end + test "Phoenix logs filter authentication and handover secrets" do + assert Phoenix.Logger.filter_values(%{ + "password" => "not-logged", + "_csrf_token" => "not-logged", + "token" => "not-logged", + "handover" => %{"code" => "123456"}, + "email" => "visible@example.test" + }) == %{ + "password" => "[FILTERED]", + "_csrf_token" => "[FILTERED]", + "token" => "[FILTERED]", + "handover" => %{"code" => "[FILTERED]"}, + "email" => "visible@example.test" + } + end + test "GET / selects Russian locale", %{conn: conn} do conn = get(conn, ~p"/?locale=ru") assert html_response(conn, 200) =~ "Помощь может быть ближе, чем кажется." diff --git a/test/who_need_help_web/controllers/user_registration_controller_test.exs b/test/who_need_help_web/controllers/user_registration_controller_test.exs index 80b5470..6ea287b 100644 --- a/test/who_need_help_web/controllers/user_registration_controller_test.exs +++ b/test/who_need_help_web/controllers/user_registration_controller_test.exs @@ -2,11 +2,13 @@ defmodule WhoNeedHelpWeb.UserRegistrationControllerTest do use WhoNeedHelpWeb.ConnCase, async: true import WhoNeedHelp.AccountsFixtures + import Swoosh.TestAssertions describe "GET /users/register" do test "renders registration page", %{conn: conn} do conn = get(conn, ~p"/users/register") response = html_response(conn, 200) + assert get_resp_header(conn, "cache-control") == ["no-store"] assert response =~ "Register" assert response =~ ~p"/users/log-in" assert response =~ ~p"/users/register" @@ -20,6 +22,28 @@ defmodule WhoNeedHelpWeb.UserRegistrationControllerTest do end describe "POST /users/register" do + test "returns bad request for malformed parameters", %{conn: conn} do + conn = post(conn, ~p"/users/register", %{"user" => "invalid"}) + + assert html_response(conn, 400) =~ "The registration form is invalid." + assert get_resp_header(conn, "cache-control") == ["no-store"] + end + + test "renders validation errors when email has a forged nested type", %{conn: conn} do + conn = + post(conn, ~p"/users/register", %{ + "user" => %{ + "email" => %{"nested" => "value"}, + "display_name" => "Malformed email", + "locale" => "en", + "terms_accepted" => "true" + } + }) + + assert html_response(conn, 200) =~ "blank" + refute get_session(conn, :user_token) + end + @tag :capture_log test "creates account but does not log in", %{conn: conn} do email = unique_user_email() @@ -33,7 +57,25 @@ defmodule WhoNeedHelpWeb.UserRegistrationControllerTest do assert redirected_to(conn) == ~p"/users/log-in" assert conn.assigns.flash["info"] =~ - ~r/An email was sent to .*, please access it to confirm your account/ + "If this address can be registered or signed in, login instructions will arrive shortly." + + assert_email_sent() + end + + test "does not reveal whether a valid email is already registered", %{conn: conn} do + user = user_fixture() + params = %{"user" => valid_user_attributes(email: user.email)} + + existing_conn = post(conn, ~p"/users/register", params) + + assert redirected_to(existing_conn) == ~p"/users/log-in" + + assert existing_conn.assigns.flash["info"] == + "If this address can be registered or signed in, login instructions will arrive shortly." + + assert_email_sent(fn email -> + Enum.any?(email.to, fn {_name, address} -> address == user.email end) + end) end test "render errors for invalid data", %{conn: conn} do diff --git a/test/who_need_help_web/controllers/user_session_controller_test.exs b/test/who_need_help_web/controllers/user_session_controller_test.exs index 3ec53df..a4f20e3 100644 --- a/test/who_need_help_web/controllers/user_session_controller_test.exs +++ b/test/who_need_help_web/controllers/user_session_controller_test.exs @@ -2,6 +2,7 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do use WhoNeedHelpWeb.ConnCase, async: false import WhoNeedHelp.AccountsFixtures + import Swoosh.TestAssertions alias WhoNeedHelp.Accounts setup do @@ -12,9 +13,12 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do test "renders login page", %{conn: conn} do conn = get(conn, ~p"/users/log-in") response = html_response(conn, 200) + assert get_resp_header(conn, "cache-control") == ["no-store"] assert response =~ "Log in" assert response =~ ~p"/users/register" assert response =~ "Log in with email" + assert response =~ ~s(id="magic-link-fragment-form") + assert response =~ ~s(hidden) end test "renders login page with email filled in (sudo mode)", %{conn: conn, user: user} do @@ -41,38 +45,6 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do end end - describe "GET /users/log-in/:token" do - test "renders confirmation page for unconfirmed user", %{conn: conn, unconfirmed_user: user} do - token = - extract_user_token(fn url -> - Accounts.deliver_login_instructions(user, url) - end) - - conn = get(conn, ~p"/users/log-in/#{token}") - assert html_response(conn, 200) =~ "Confirm and stay logged in" - end - - test "renders login page for confirmed user", %{conn: conn, user: user} do - token = - extract_user_token(fn url -> - Accounts.deliver_login_instructions(user, url) - end) - - conn = get(conn, ~p"/users/log-in/#{token}") - html = html_response(conn, 200) - refute html =~ "Confirm my account" - assert html =~ "Log in" - end - - test "raises error for invalid token", %{conn: conn} do - conn = get(conn, ~p"/users/log-in/invalid-token") - assert redirected_to(conn) == ~p"/users/log-in" - - assert Phoenix.Flash.get(conn.assigns.flash, :error) == - "Magic link is invalid or it has expired." - end - end - describe "POST /users/log-in - email and password" do test "logs the user in", %{conn: conn, user: user} do user = set_password(user) @@ -189,7 +161,16 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do end describe "POST /users/log-in - magic link" do + test "returns bad request for malformed parameters", %{conn: conn} do + conn = post(conn, ~p"/users/log-in", %{"user" => %{"email" => ["invalid"]}}) + + assert html_response(conn, 400) =~ "The sign-in form is invalid." + assert get_resp_header(conn, "cache-control") == ["no-store"] + end + test "sends magic link email when user exists", %{conn: conn, user: user} do + assert_email_sent() + conn = post(conn, ~p"/users/log-in", %{ "user" => %{"email" => user.email} @@ -197,6 +178,11 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" assert WhoNeedHelp.Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "login" + + assert_email_sent(fn email -> + email.text_body =~ "/users/log-in#token=" and + not String.contains?(email.text_body, "/users/log-in/") + end) end test "does not send a magic link for a suspended user", %{conn: conn, user: user} do diff --git a/test/who_need_help_web/controllers/user_settings_controller_test.exs b/test/who_need_help_web/controllers/user_settings_controller_test.exs index 0ab55a8..1a380b7 100644 --- a/test/who_need_help_web/controllers/user_settings_controller_test.exs +++ b/test/who_need_help_web/controllers/user_settings_controller_test.exs @@ -3,6 +3,7 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do alias WhoNeedHelp.Accounts import WhoNeedHelp.AccountsFixtures + import Swoosh.TestAssertions setup :register_and_log_in_user @@ -70,18 +71,29 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do assert response =~ "Settings" assert response =~ "should be at least 12 character(s)" assert response =~ "does not match password" + refute response =~ ~s(value="too short") + refute response =~ ~s(value="does not match") assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token) end end describe "PUT /users/settings (change email form)" do + test "returns bad request for malformed parameters", %{conn: conn} do + conn = put(conn, ~p"/users/settings", %{"action" => "unknown"}) + + assert html_response(conn, 400) =~ "The settings form is invalid." + end + @tag :capture_log test "updates the user email", %{conn: conn, user: user} do + assert_email_sent() + changed_email = unique_user_email() + conn = put(conn, ~p"/users/settings", %{ "action" => "update_email", - "user" => %{"email" => unique_user_email()} + "user" => %{"email" => changed_email} }) assert redirected_to(conn) == ~p"/users/settings" @@ -89,6 +101,11 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "A link to confirm your email" + assert_email_sent(fn email -> + email.text_body =~ "/users/settings/confirm-email#token=" and + not String.contains?(email.text_body, "/users/settings/confirm-email/") + end) + assert Accounts.get_user_by_email(user.email) end @@ -103,9 +120,29 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do assert response =~ "Settings" assert response =~ "must have the @ sign and no spaces" end + + test "rate limits repeated email-change instructions", %{conn: conn} do + previous = Application.get_env(:who_need_help, :rate_limit_policies) + + Application.put_env(:who_need_help, :rate_limit_policies, %{ + "email_change_email" => %{limit: 1, window_seconds: 60} + }) + + on_exit(fn -> Application.put_env(:who_need_help, :rate_limit_policies, previous) end) + + email = unique_user_email() + params = %{"action" => "update_email", "user" => %{"email" => email}} + + assert redirected_to(put(conn, ~p"/users/settings", params)) == ~p"/users/settings" + + limited_conn = put(conn, ~p"/users/settings", params) + response = html_response(limited_conn, 429) + + assert response =~ "Too many email-change requests" + end end - describe "GET /users/settings/confirm-email/:token" do + describe "POST /users/settings/confirm-email" do setup %{user: user} do email = unique_user_email() @@ -118,7 +155,12 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do end test "updates the user email once", %{conn: conn, user: user, token: token, email: email} do - conn = get(conn, ~p"/users/settings/confirm-email/#{token}") + page_conn = get(conn, ~p"/users/settings/confirm-email") + page_response = html_response(page_conn, 200) + assert page_response =~ ~s(id="email-change-fragment-form") + assert page_response =~ ~s(hidden) + + conn = post(conn, ~p"/users/settings/confirm-email", %{"token" => token}) assert redirected_to(conn) == ~p"/users/settings" assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ @@ -127,7 +169,7 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do refute Accounts.get_user_by_email(user.email) assert Accounts.get_user_by_email(email) - conn = get(conn, ~p"/users/settings/confirm-email/#{token}") + conn = post(conn, ~p"/users/settings/confirm-email", %{"token" => token}) assert redirected_to(conn) == ~p"/users/settings" @@ -136,7 +178,31 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do end test "does not update email with invalid token", %{conn: conn, user: user} do - conn = get(conn, ~p"/users/settings/confirm-email/oops") + conn = post(conn, ~p"/users/settings/confirm-email", %{"token" => "oops"}) + assert redirected_to(conn) == ~p"/users/settings" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ + "Email change link is invalid or it has expired" + + assert Accounts.get_user_by_email(user.email) + end + + test "does not update email without a token", %{conn: conn, user: user} do + conn = post(conn, ~p"/users/settings/confirm-email", %{}) + assert redirected_to(conn) == ~p"/users/settings" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ + "Email change link is invalid or it has expired" + + assert Accounts.get_user_by_email(user.email) + end + + test "does not raise when the token has a forged nested type", %{conn: conn, user: user} do + conn = + post(conn, ~p"/users/settings/confirm-email", %{ + "token" => %{"nested" => "value"} + }) + assert redirected_to(conn) == ~p"/users/settings" assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ @@ -147,8 +213,19 @@ defmodule WhoNeedHelpWeb.UserSettingsControllerTest do test "redirects if user is not logged in", %{token: token} do conn = build_conn() - conn = get(conn, ~p"/users/settings/confirm-email/#{token}") + conn = post(conn, ~p"/users/settings/confirm-email", %{"token" => token}) assert redirected_to(conn) == ~p"/users/log-in" end + + @tag token_authenticated_at: DateTime.add(DateTime.utc_now(:second), -11, :minute) + test "accepts a valid token outside sudo mode", %{ + conn: conn, + token: token, + email: email + } do + conn = post(conn, ~p"/users/settings/confirm-email", %{"token" => token}) + assert redirected_to(conn) == ~p"/users/settings" + assert Accounts.get_user_by_email(email) + end end end diff --git a/test/who_need_help_web/live/mutual_aid_live_test.exs b/test/who_need_help_web/live/mutual_aid_live_test.exs index b6c77fa..5264700 100644 --- a/test/who_need_help_web/live/mutual_aid_live_test.exs +++ b/test/who_need_help_web/live/mutual_aid_live_test.exs @@ -23,6 +23,32 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert html =~ "Створити термінову заявку" end + test "profile social-link creation uses the shared action limiter", %{conn: conn} do + previous = Application.get_env(:who_need_help, :rate_limit_policies) + + Application.put_env(:who_need_help, :rate_limit_policies, %{ + "add_social" => %{limit: 1, window_seconds: 60} + }) + + on_exit(fn -> Application.put_env(:who_need_help, :rate_limit_policies, previous) end) + + {:ok, view, _html} = live(conn, ~p"/profile") + + assert render_submit(view, "add-social", %{ + "social_identity" => %{ + "provider" => "telegram", + "profile_url" => "https://t.me/rate_limit_first" + } + }) =~ "Social link added as unverified." + + assert render_submit(view, "add-social", %{ + "social_identity" => %{ + "provider" => "github", + "profile_url" => "https://github.com/rate-limit-second" + } + }) =~ "Too many actions in the configured time window." + end + test "invalid request and activity identifiers redirect without crashing", %{conn: conn} do assert {:error, {:live_redirect, %{to: "/requests"}}} = live(conn, "/requests/not-a-uuid") @@ -59,6 +85,38 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do refute render(view) =~ "Realtime medicine pickup" end + test "request realtime updates keep a bounded window without skipping pagination" do + category = Catalog.seed_defaults() + requester = user_fixture(display_name: "Bounded realtime requester") + viewer = user_fixture(display_name: "Bounded realtime viewer") + {:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/requests") + + requests = + for index <- 1..25 do + attrs = + category + |> request_attrs() + |> Map.put("title", "Bounded realtime request #{index}") + |> Map.put( + "expires_at", + DateTime.utc_now(:second) |> DateTime.add(3 * 60 * 60 + index, :second) + ) + + {:ok, request} = Help.create_request(Accounts.Scope.for_user(requester), attrs) + request + end + + render(view) + first = List.first(requests) + overflow = List.last(requests) + + assert has_element?(view, "#open-request-#{first.id}") + refute has_element?(view, "#open-request-#{overflow.id}") + + view |> element("button", "Load more") |> render_click() + assert has_element?(view, "#open-request-#{overflow.id}") + end + test "activity index applies PubSub updates without a full page reload" do Catalog.seed_defaults() organizer = user_fixture(display_name: "Realtime organizer") @@ -89,6 +147,45 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert render(view) =~ "Realtime coffee meetup" end + test "activity realtime updates keep a bounded window without skipping pagination" do + Catalog.seed_defaults() + organizer = user_fixture(display_name: "Bounded realtime organizer") + viewer = user_fixture(display_name: "Bounded realtime activity viewer") + + category = + Catalog.list_categories(:activity) + |> Enum.find(&(&1.slug == "coffee-meetup")) + + {:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/activities") + + activities = + for index <- 1..25 do + attrs = + category + |> activity_attrs() + |> Map.put("title", "Bounded realtime activity #{index}") + |> Map.put( + "starts_at", + DateTime.utc_now(:second) |> DateTime.add(3 * 60 * 60 + index, :second) + ) + + {:ok, activity} = + Activities.create_activity(Accounts.Scope.for_user(organizer), attrs) + + activity + end + + render(view) + first = List.first(activities) + overflow = List.last(activities) + + assert has_element?(view, "#open-activity-#{first.id}") + refute has_element?(view, "#open-activity-#{overflow.id}") + + view |> element("button", "Load more") |> render_click() + assert has_element?(view, "#open-activity-#{overflow.id}") + end + test "new request form is driven by category structured fields", %{conn: conn} do category = Catalog.seed_defaults() {:ok, view, html} = live(conn, ~p"/requests/new") @@ -259,6 +356,34 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert html =~ "I will be there." assert render(organizer_view) =~ "I will be there." + now = DateTime.utc_now(:second) + + for index <- 1..51 do + body = + case index do + 1 -> "oldest-realtime-activity-message" + 51 -> "newest-realtime-activity-message" + _ -> "bounded activity message #{index}" + end + + send( + organizer_view.pid, + {:activity_message, + %WhoNeedHelp.Activities.Message{ + id: Ecto.UUID.generate(), + activity_id: activity.id, + sender_id: participant.id, + sender: participant, + body: body, + inserted_at: DateTime.add(now, index, :second) + }} + ) + end + + bounded_activity_html = render(organizer_view) + refute bounded_activity_html =~ "oldest-realtime-activity-message" + assert bounded_activity_html =~ "newest-realtime-activity-message" + stop_live_view(organizer_view) stop_live_view(participant_view) end @@ -294,6 +419,35 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert_push_event(helper_view, "reset-message-form", %{id: "message-form"}) assert render(requester_view) =~ "I am on my way to the pharmacy." + now = DateTime.utc_now(:second) + assignment_id = Repo.get_by!(WhoNeedHelp.Help.Assignment, request_id: request.id).id + + for index <- 1..51 do + body = + case index do + 1 -> "oldest-realtime-help-message" + 51 -> "newest-realtime-help-message" + _ -> "bounded help message #{index}" + end + + send( + requester_view.pid, + {:new_message, + %WhoNeedHelp.Messaging.Message{ + id: Ecto.UUID.generate(), + assignment_id: assignment_id, + sender_id: helper.id, + sender: helper, + body: body, + inserted_at: DateTime.add(now, index, :second) + }} + ) + end + + bounded_help_html = render(requester_view) + refute bounded_help_html =~ "oldest-realtime-help-message" + assert bounded_help_html =~ "newest-realtime-help-message" + send( requester_view.pid, {:position_updated, helper.id, @@ -431,6 +585,23 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do } end + defp activity_attrs(category) do + %{ + "title" => "Public coffee meetup", + "description" => "Meet at a public café for a short conversation.", + "structured_data" => %{"setting" => "cafe"}, + "location_label" => "Central square", + "latitude" => "50.4501", + "longitude" => "30.5234", + "location_visibility" => "approximate_public", + "starts_at" => DateTime.utc_now(:second) |> DateTime.add(3, :hour), + "join_deadline" => DateTime.utc_now(:second) |> DateTime.add(2, :hour), + "capacity" => 3, + "category_id" => category.id, + "safety_confirmed" => true + } + end + defp stop_live_view(%Phoenix.LiveViewTest.View{ proxy: {_ref, _topic, proxy_pid} }) do