diff --git a/.env.e2e.example b/.env.e2e.example
index aef42c5..e4d5aa4 100644
--- a/.env.e2e.example
+++ b/.env.e2e.example
@@ -54,6 +54,11 @@ GITHUB_OAUTH_TOKEN_URL=
GITHUB_OAUTH_USER_URL=
GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
+GOOGLE_OAUTH_CLIENT_ID=
+GOOGLE_OAUTH_CLIENT_SECRET=
+GOOGLE_OAUTH_BASE_URL=
+GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
+GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
E2E_BASE_URL=https://proxy
E2E_MAILPIT_URL=http://mailpit:8025
diff --git a/.env.example b/.env.example
index 8e2df3b..671e506 100644
--- a/.env.example
+++ b/.env.example
@@ -52,6 +52,17 @@ GITHUB_OAUTH_USER_URL=
GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
+# Optional Google OpenID Connect registration and sign-in. Leave both empty
+# until a Google OAuth Web client exists. Its callback URL must be:
+# https://YOUR_PHX_HOST/auth/google/callback
+GOOGLE_OAUTH_CLIENT_ID=
+GOOGLE_OAUTH_CLIENT_SECRET=
+# Leave endpoint and timeout overrides empty for Google's discovery endpoint
+# and Req defaults. The base URL override exists for isolated protocol tests.
+GOOGLE_OAUTH_BASE_URL=
+GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
+GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
+
# Optional provider-neutral HTTP push boundary. Leave both endpoint and token
# empty to disable product push jobs. When enabled, all four numeric values are
# required deployment inputs; the project does not claim universal production
diff --git a/.env.load.example b/.env.load.example
index 6c23372..a42e246 100644
--- a/.env.load.example
+++ b/.env.load.example
@@ -55,6 +55,11 @@ GITHUB_OAUTH_TOKEN_URL=
GITHUB_OAUTH_USER_URL=
GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
+GOOGLE_OAUTH_CLIENT_ID=
+GOOGLE_OAUTH_CLIENT_SECRET=
+GOOGLE_OAUTH_BASE_URL=
+GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS=
+GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS=
# These are reproducible experiment inputs, not capacity requirements,
# production traffic forecasts, alert thresholds, or recommended limits.
diff --git a/README.md b/README.md
index 6bb82ab..f2612c3 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,9 @@ thank-you link; money goes directly between users outside the platform.
## What is implemented
-- Phoenix 1.8 LiveView application with email magic-link/password auth and 18+
- self-attestation.
+- Phoenix 1.8 LiveView application with passwordless email magic links,
+ optional passwords, optional Google OpenID Connect registration/sign-in, and
+ 18+ self-attestation.
- Data-driven, translated category tree with validated per-category fields,
seven selectable urgent/roadside scenarios, community proposals/votes, and
human approve/reject/merge tools.
@@ -140,7 +141,7 @@ restores it into a new temporary database, checks corruption and interruption
failure paths, removes those temporary buckets, and retains the successful
encrypted bucket in local MinIO. It never writes a plaintext dump to the host.
-Exercise the real OAuth/SMTP protocol clients and the provider-neutral push
+Exercise the real GitHub OAuth, Google OIDC, SMTP protocol clients, and the provider-neutral push
boundary entirely inside an isolated Docker network:
```bash
@@ -178,8 +179,9 @@ printing them, writes mode `0600`, and refuses to replace an existing file:
```
Configure the verified reverse-proxy source IP/CIDR and transactional SMTP
-provider in that file, then validate its structure and the production Compose
-render:
+provider in that file. Email registration and magic-link login are unusable for
+real recipients until that relay and its accepted sender are configured.
+Then validate the file structure and the production Compose render:
```bash
./scripts/validate-production-env.sh .env.production whoneedhelp.com
@@ -207,6 +209,39 @@ docker compose up -d --wait
This preserves the named PostgreSQL volume, but invalidates existing browser
sessions and changes handover codes for active local requests.
+To rotate only the local PostgreSQL role and matching `DATABASE_URL`, without
+changing Phoenix sessions or handover codes, use:
+
+```bash
+./scripts/rotate-local-secrets.sh --database-only
+docker compose up -d --wait
+```
+
+### Optional Google registration and sign-in
+
+Create a Google OAuth 2.0 Web application only for an owned HTTPS origin. Add
+this exact authorized redirect URI in Google Cloud:
+
+```text
+https://YOUR_PHX_HOST/auth/google/callback
+```
+
+Set both `GOOGLE_OAUTH_CLIENT_ID` and `GOOGLE_OAUTH_CLIENT_SECRET` in the
+ignored `.env` or deployment Secret, then recreate the application containers.
+If both values are empty, the Google buttons remain visible but disabled with
+an explanation. A partial pair is rejected at startup and by the production
+environment validator.
+
+The flow requests `openid email profile`, verifies the provider email claim,
+uses state, nonce, and PKCE, and discards provider tokens. Google registration
+creates a confirmed local account only after the user accepts the 18+ safety
+terms. Google login works only for an identity already linked to that local
+account. An existing local account is never merged merely because Google
+returns the same email; sign in by email or password and connect Google from
+the sudo-protected account settings page instead. Leave
+`GOOGLE_OAUTH_BASE_URL` and the Google HTTP timeout variables empty outside the
+isolated protocol drill.
+
### Optional verified GitHub linking
Create a GitHub OAuth App with this exact callback URL for the active public
diff --git a/compose.external-boundaries.yaml b/compose.external-boundaries.yaml
index e74a555..81d1056 100644
--- a/compose.external-boundaries.yaml
+++ b/compose.external-boundaries.yaml
@@ -17,6 +17,11 @@ x-boundary-app-environment: &boundary-app-environment
ALLOW_INSECURE_EXTERNAL_HTTP: "true"
GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS: "100"
GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS: "100"
+ GOOGLE_OAUTH_CLIENT_ID: ${EXTERNAL_OAUTH_CLIENT_ID:?Set EXTERNAL_OAUTH_CLIENT_ID}
+ GOOGLE_OAUTH_CLIENT_SECRET: ${EXTERNAL_OAUTH_CLIENT_SECRET:?Set EXTERNAL_OAUTH_CLIENT_SECRET}
+ GOOGLE_OAUTH_BASE_URL: http://external-mock:8080
+ GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS: "100"
+ GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS: "100"
SMTP_RELAY: external-mock
SMTP_PORT: "2525"
SMTP_USERNAME: ""
diff --git a/compose.yaml b/compose.yaml
index 794b962..b9750de 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -37,6 +37,11 @@ x-app-environment: &app-environment
GITHUB_OAUTH_USER_URL: ${GITHUB_OAUTH_USER_URL:-}
GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS: ${GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS:-}
GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS: ${GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS:-}
+ GOOGLE_OAUTH_CLIENT_ID: ${GOOGLE_OAUTH_CLIENT_ID:-}
+ GOOGLE_OAUTH_CLIENT_SECRET: ${GOOGLE_OAUTH_CLIENT_SECRET:-}
+ GOOGLE_OAUTH_BASE_URL: ${GOOGLE_OAUTH_BASE_URL:-}
+ GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS: ${GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS:-}
+ GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS: ${GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS:-}
PUSH_HTTP_ENDPOINT: ${PUSH_HTTP_ENDPOINT:-}
PUSH_HTTP_BEARER_TOKEN: ${PUSH_HTTP_BEARER_TOKEN:-}
PUSH_HTTP_MAX_ATTEMPTS: ${PUSH_HTTP_MAX_ATTEMPTS:-}
diff --git a/config/runtime.exs b/config/runtime.exs
index cd2aa31..5be771e 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -133,20 +133,21 @@ oauth_endpoint = fn name, default ->
end
end
-oauth_http_options =
+oauth_http_options = fn prefix ->
[retry: false]
|> then(fn options ->
- case optional_positive_integer.("GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS") do
+ case optional_positive_integer.("#{prefix}_OAUTH_HTTP_RECEIVE_TIMEOUT_MS") do
nil -> options
timeout -> Keyword.put(options, :receive_timeout, timeout)
end
end)
|> then(fn options ->
- case optional_positive_integer.("GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS") do
+ case optional_positive_integer.("#{prefix}_OAUTH_HTTP_CONNECT_TIMEOUT_MS") do
nil -> options
timeout -> Keyword.put(options, :connect_options, timeout: timeout)
end
end)
+end
github_oauth =
case {
@@ -172,7 +173,7 @@ github_oauth =
"https://github.com/login/oauth/access_token"
),
user_url: oauth_endpoint.("GITHUB_OAUTH_USER_URL", "https://api.github.com/user"),
- http_adapter: {Assent.HTTPAdapter.Req, oauth_http_options}
+ http_adapter: {Assent.HTTPAdapter.Req, oauth_http_options.("GITHUB")}
]
}
@@ -187,6 +188,33 @@ github_oauth =
config :who_need_help, :social_oauth, github_oauth
+google_auth =
+ case {
+ System.get_env("GOOGLE_OAUTH_CLIENT_ID"),
+ System.get_env("GOOGLE_OAUTH_CLIENT_SECRET")
+ } do
+ {client_id, client_secret}
+ when is_binary(client_id) and client_id != "" and is_binary(client_secret) and
+ client_secret != "" ->
+ [
+ client_id: client_id,
+ client_secret: client_secret,
+ base_url: oauth_endpoint.("GOOGLE_OAUTH_BASE_URL", "https://accounts.google.com/"),
+ authorization_params: [scope: "email profile"],
+ http_adapter: {Assent.HTTPAdapter.Req, oauth_http_options.("GOOGLE")}
+ ]
+
+ {client_id, client_secret} when client_id in [nil, ""] and client_secret in [nil, ""] ->
+ []
+
+ _partial_configuration ->
+ raise """
+ GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET must either both be set or both be empty.
+ """
+ end
+
+config :who_need_help, :google_auth, google_auth
+
push_configuration =
case {
System.get_env("PUSH_HTTP_ENDPOINT"),
@@ -366,6 +394,23 @@ if config_env() == :prod do
other -> raise "SMTP_SSL must be true, false, 1, or 0; got #{inspect(other)}"
end
+ smtp_username = System.get_env("SMTP_USERNAME")
+ smtp_password = System.get_env("SMTP_PASSWORD")
+ smtp_username_set? = is_binary(smtp_username) and smtp_username != ""
+ smtp_password_set? = is_binary(smtp_password) and smtp_password != ""
+
+ if smtp_username_set? != smtp_password_set? do
+ raise "SMTP_USERNAME and SMTP_PASSWORD must either both be set or both be empty."
+ end
+
+ if smtp_auth == :always and not (smtp_username_set? and smtp_password_set?) do
+ raise "SMTP_USERNAME and SMTP_PASSWORD are required when SMTP_AUTH is always."
+ end
+
+ if smtp_ssl and smtp_tls != :never do
+ raise "SMTP_TLS must be never when SMTP_SSL enables an implicit TLS connection."
+ end
+
smtp_config =
[
adapter: Swoosh.Adapters.SMTP,
@@ -376,13 +421,13 @@ if config_env() == :prod do
ssl: smtp_ssl
]
|> then(fn config ->
- case System.get_env("SMTP_USERNAME") do
+ case smtp_username do
value when is_binary(value) and value != "" -> Keyword.put(config, :username, value)
_ -> config
end
end)
|> then(fn config ->
- case System.get_env("SMTP_PASSWORD") do
+ case smtp_password do
value when is_binary(value) and value != "" -> Keyword.put(config, :password, value)
_ -> config
end
diff --git a/config/test.exs b/config/test.exs
index b4d3cae..8046bd8 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -29,6 +29,7 @@ config :who_need_help, WhoNeedHelpWeb.Endpoint,
# In test we don't send emails
config :who_need_help, WhoNeedHelp.Mailer, adapter: Swoosh.Adapters.Test
config :who_need_help, :social_oauth_adapter, WhoNeedHelp.SocialOAuthFake
+config :who_need_help, :google_auth_adapter, WhoNeedHelp.GoogleAuthFake
config :who_need_help, :metrics_token, "test-metrics-token"
# Disable swoosh api client as it is only required for production adapters
diff --git a/deploy/helm/who-need-help/values.yaml b/deploy/helm/who-need-help/values.yaml
index 97e89ad..6816a8f 100644
--- a/deploy/helm/who-need-help/values.yaml
+++ b/deploy/helm/who-need-help/values.yaml
@@ -39,7 +39,9 @@ app:
# Required. The Secret must contain DATABASE_URL, SECRET_KEY_BASE,
# HANDOVER_SECRET, RELEASE_COOKIE, and METRICS_TOKEN. It may also contain both
# GITHUB_OAUTH_CLIENT_ID and GITHUB_OAUTH_CLIENT_SECRET to enable verified
-# GitHub linking. Push is opt-in: provide PUSH_HTTP_ENDPOINT,
+# GitHub linking, and both GOOGLE_OAUTH_CLIENT_ID and
+# GOOGLE_OAUTH_CLIENT_SECRET to enable Google registration/sign-in. Push is
+# opt-in: provide PUSH_HTTP_ENDPOINT,
# PUSH_HTTP_BEARER_TOKEN, PUSH_HTTP_MAX_ATTEMPTS,
# PUSH_HTTP_RECEIVE_TIMEOUT_MS, PUSH_HTTP_CONNECT_TIMEOUT_MS, and
# PUSH_HTTP_RETRY_DELAY_MS as one complete set.
diff --git a/docs/architecture.md b/docs/architecture.md
index 28f4708..852f49e 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -47,8 +47,13 @@ and are not represented as complete.
## Application boundaries
-- `Accounts`: users, authentication, social identities, privacy preferences,
- blocks, and roles.
+- `Accounts`: users, email/password authentication, private authentication
+ identities, public social identities, privacy preferences, blocks, and
+ roles.
+- `GoogleAuth`: optional Google OpenID Connect boundary for registration,
+ sign-in, and account linking. It uses state, nonce, and PKCE, returns only a
+ normalized verified-email identity, and does not expose provider tokens to
+ controllers or persistence.
- `SocialOAuth`: optional provider boundary for verified social linking. The
GitHub adapter uses OAuth state and PKCE, returns only normalized identity
attributes to the controller, and does not expose the provider token to the
diff --git a/docs/operations.md b/docs/operations.md
index 19cd071..9dbce4e 100644
--- a/docs/operations.md
+++ b/docs/operations.md
@@ -59,7 +59,10 @@ do not copy the temporary VPN value into an unrelated server.
Configure the transactional SMTP relay and sender accepted by that provider.
Provider-specific auth, TLS, ports, and credentials can be supplied to the
initializer through the documented `PRODUCTION_SMTP_*` environment values or
-edited in the resulting ignored file. Then run:
+edited in the resulting ignored file. If Google registration/sign-in is
+enabled, also set both Google Web client credentials and register
+`https://YOUR_PHX_HOST/auth/google/callback` as the exact authorized redirect
+URI. Leave both credentials empty to keep the feature disabled. Then run:
```bash
./scripts/validate-production-env.sh .env.production whoneedhelp.com
@@ -73,8 +76,9 @@ docker compose \
The production override keeps Mailpit behind its inactive `local-mail` profile,
so public registration cannot appear to succeed while mail is only retained
locally. The validator checks file ownership/mode, origin consistency,
-template markers, independent generated secrets, supported SMTP values, the
-generated database URL, and the final Compose render without printing secrets.
+template markers, independent generated secrets, supported SMTP values, a
+complete-or-empty Google credential pair, the generated database URL, and the
+final Compose render without printing secrets.
It does not contact DNS, TLS, SMTP, the reverse proxy, or the application.
After deployment, verify `/healthz/ready`, inspect all replica health and logs,
register a unique address through the public browser, receive its message at
diff --git a/docs/product-spec.md b/docs/product-spec.md
index efc9747..a85383d 100644
--- a/docs/product-spec.md
+++ b/docs/product-spec.md
@@ -38,7 +38,8 @@ helper per request. Every state transition is authorized and recorded.
## MVP scope
-- Email magic-link/password authentication.
+- Passwordless email magic-link authentication, optional passwords, and
+ optional Google OpenID Connect registration/sign-in.
- English default interface plus Ukrainian and Russian translations.
- Medicine pickup/delivery requests.
- Safe, non-emergency fuel delivery, car-wheel help, bicycle and motorcycle
@@ -77,8 +78,9 @@ helper per request. Every state transition is authorized and recorded.
- Multiple simultaneous helpers on one request.
- Production-signed Android release, store publication, and native iOS
application.
-- OAuth verification for providers other than GitHub and automated
- social-network identity scoring.
+- OAuth verification of public social-profile badges for providers other than
+ GitHub, and automated social-network identity scoring. Google authentication
+ is a private sign-in identity, not a public verified social-profile badge.
- Automatic punitive abuse enforcement and unapproved production thresholds.
- Claims that identity, safety, or fraud prevention is perfect.
@@ -96,6 +98,12 @@ configured GitHub OAuth flow can set `verified_at` after provider authorization;
one provider account cannot be linked to two local accounts. The provider
access token is discarded rather than stored.
+Google OpenID Connect identities are stored separately from public social
+links. A verified Google email can create a confirmed local account after the
+same 18+ safety acknowledgement, or can be connected from a recently
+reauthenticated account-settings session. Matching email addresses never merge
+accounts automatically, and Google provider tokens are not persisted.
+
## Privacy settings
Every user chooses one location visibility level:
diff --git a/docs/verification.md b/docs/verification.md
index 0b264c1..2ea4679 100644
--- a/docs/verification.md
+++ b/docs/verification.md
@@ -16,16 +16,38 @@ results from product limits and unknown production properties.
| Consent-driven live tracking | Implemented and cross-client verified | On API 37, Android started `TrackingService` as a location foreground service with a persistent Stop notification. After Home minimized the Activity, an emulator coordinate change reached PostGIS. Notification Stop removed the service, notification, active session, and raw position. | Browsers stop with the page. Android has no `ACCESS_BACKGROUND_LOCATION`, unattended start, or route history. |
| Privacy settings | Implemented and browser-verified | The profile exposed hidden, approximate public, exact for active match, and explicit exact-public options. Blocking and current-position cleanup have automated tests. | Exact public location remains a user opt-in; legal privacy and retention text still requires jurisdiction-specific review before launch. |
| Reputation and anti-abuse | Implemented at MVP level | Handover codes, two-party completion, double-blind reviews, unique-counterpart ranking, optional movement/proximity evidence, reports, blocks, abuse signals, and moderator audit paths have automated tests. | The system is not bot-proof and does not claim identity verification. No punitive numeric policy is enabled without measured and approved thresholds. |
-| Social profiles | Manual links implemented; optional GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record; the current 186-test suite includes callback replay/state checks. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. |
+| Account registration and sign-in | Implemented and browser-verified | Email registration sends a confirmation magic link and does not require a password. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 260-test suite. A headed Chrome run completed registration, confirmation through Mailpit, password setup, logout, password login, and localized settings with zero console warnings or errors. | The local Compose environment captures email in Mailpit. A production SMTP relay and a real Google Web client are not configured or externally verified. |
+| Social profiles | Manual links implemented; optional GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. |
| Voluntary thanks | Implemented as an external optional link | A helper can expose an optional link after completion; the UI states that the platform does not process the payment. | The platform does not provide payments, escrow, refunds, tax reporting, or payment guarantees. |
| Android client | Local and public-staging clients implemented and emulator-verified | The native packages `org.whoneedhelp.mobile.debug` and `org.whoneedhelp.mobile.staging` launch the same authenticated LiveView app. Seven lifecycle, permission, deep-link, foreground tracking, recoverable main-page failure, notification-Stop, and Activity-destruction tests passed on each of API 30, 34, and 37. The API 37 staging smoke asserted the public home and Safety DOM over HTTPS. A run-scoped Android/browser staging test passed login, private chat in both directions, foreground tracking, live marker appearance/removal, and exact cleanup. | Production signing, Play Store publication, verified Android App Links, unattended/background-permission tracking, and iOS are not implemented. |
| Multiple web/worker instances | Implemented and locally failure/rollout-verified | The final isolated Compose drill passed BEAM crashes and sequential replacement with 3 web/2 worker replicas: all five nodes joined, PubSub passed, and 744/744 readiness requests succeeded. The project-owned kind cluster replaced all 2 web/2 worker pod UIDs under `maxUnavailable=0`; all four replacement pods joined, PubSub passed, and 363/363 samples ultimately succeeded. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. |
| Local observability | Implemented and protocol-verified | Pinned Prometheus scraped the exact 3 web and 2 worker targets with a file Bearer credential; Grafana provisioned a healthy datasource and ten-panel web/worker/BEAM/Ecto/Oban dashboard; Alertmanager delivered firing and resolved webhooks for an induced scoped replica stop. | Local delivery does not establish production retention, notification-provider reliability, on-call policy, or measured alert thresholds. |
| Encrypted local backup | Implemented and failure-verified | Pinned Restic streamed PostgreSQL custom format into pinned local MinIO with no host plaintext dump, passed full-data checking and a fresh-database restore, rejected a corrupted repository, and published no snapshot for an interrupted upload. The one-run MinIO project and volume were removed after retaining the non-secret evidence. | The drill proves the local mechanism, not off-site durability, database HA, or a production RPO/RTO/retention policy. |
-| External protocol boundaries | Implemented and locally failure-verified | The production release used its configured Assent/Req and Swoosh/gen_smtp clients against an internal-only mock; OAuth and SMTP success/rejection/retry/replay/timeout paths passed. The HTTP push boundary passed disabled, retry, rejection, timeout, and idempotency paths. Request acceptance and new-chat transactions created durable jobs processed by two Oban worker replicas; the chat event completed on Oban attempt 2 after an injected temporary failure. | This does not verify external provider availability or device delivery. FCM/APNs token registration and provider selection remain external work; SMTP exactly-once delivery is not claimed. |
+| External protocol boundaries | Implemented and locally failure-verified | The production release used its configured Assent/Req and Swoosh/gen_smtp clients against internal-only mocks. GitHub OAuth, Google OIDC discovery/authorization/token/JWKS with nonce and PKCE, and SMTP success/rejection/retry/replay/timeout paths passed. The HTTP push boundary passed disabled, retry, rejection, timeout, and idempotency paths. Request acceptance and new-chat transactions created durable jobs processed by two Oban worker replicas; the chat event completed on Oban attempt 2 after an injected temporary failure. | This does not verify real external provider availability or device delivery. Google/GitHub production clients, production SMTP, FCM/APNs token registration, and provider selection remain external work; SMTP exactly-once delivery is not claimed. |
## Reproducible checks
+- On 2026-07-20, after adding Google OpenID Connect authentication,
+ `./scripts/test.sh` and `mix precommit` each passed 260 tests with zero
+ failures. The full isolated `./scripts/quality.sh` gate also passed format,
+ compilation with warnings as errors, xref, Credo, Sobelow, Dialyzer, Hex and
+ npm audits, Compose/Helm rendering, environment validation, and configured
+ HIGH/CRITICAL source/image scans.
+- The Google boundary drill used the production Assent adapter against a local
+ OIDC server with discovery, authorization, token, signed ID token, JWKS,
+ state, nonce, PKCE, unverified-email rejection, and one-time callback replay
+ checks. It passed together with GitHub OAuth, SMTP, push, and two-worker Oban
+ paths. Evidence is retained at
+ `output/external-boundaries/google-auth-fixed-20260720`.
+- Headed Chrome verified the public temporary HTTPS origin through the
+ email-only registration form, Mailpit confirmation link, one-time login,
+ password creation, logout, password login, Russian locale selection, and
+ Google connection settings. The configured Google credential pair is empty,
+ so the UI correctly left Google actions disabled. Browser console inspection
+ reported zero errors and zero warnings. The run-owned account and its one
+ cascading login token were removed after read-only relationship checks; no
+ help, activity, message, tracking, review, or OAuth rows belonged to it. The
+ browser was left open.
- The isolated Phoenix suite completed on 2026-07-19 with 172
tests and 0 failures after cursor pagination, database aggregation, and the
full localization changes
@@ -242,6 +264,14 @@ decoded lengths were inspected: the PostgreSQL password is 64 characters and
each application secret is 128 characters. Secret values were not printed or
written to tracked files.
+Before the local `auth_identities` migration, a PostgreSQL custom-format dump
+was created at `output/backups/compose-20260720-203913.dump`; its SHA-256 is
+`ed8d663b114f3823479198005857af5501a90dacbec3f0855296327ee9cd6d20`.
+An isolated restore drill read 23 public tables, 1,488 rows, 12 applied
+migrations, and PostGIS 3.6.4 from that dump, then removed the temporary
+database. The source Compose database subsequently reported 13 applied
+migrations and an empty `auth_identities` table before browser verification.
+
## Dependency-upgrade observations
- The running Compose and kind releases reported Elixir 1.20.2 and Erlang/OTP
@@ -1031,6 +1061,10 @@ is `output/external-boundaries/preprod-gitea-pass`.
and the availability model selected for real usage.
- Configure a real transactional email provider before public registration.
The current Compose mail path terminates at local Mailpit.
+- Create a Google OAuth Web client for the final owned HTTPS origin, configure
+ its exact `/auth/google/callback` redirect, and exercise registration,
+ sign-in, and settings linking against Google if Google authentication is to
+ be enabled. The feature remains disabled when both credentials are empty.
- Configure and verify a real mobile push provider and device-token lifecycle
if native push is required. The provider-neutral HTTP boundary and product
jobs are tested; FCM/APNs device delivery is not.
diff --git a/e2e/tests/helpers.ts b/e2e/tests/helpers.ts
index 27cf3e0..1d230b0 100644
--- a/e2e/tests/helpers.ts
+++ b/e2e/tests/helpers.ts
@@ -315,12 +315,15 @@ export async function registerAndConfirm(
const page = await context.newPage();
await gotoWithTransientRetry(page, "/users/register");
- await page.getByLabel("Email").fill(email);
- await page.getByLabel("Display name").fill(displayName);
- await page
+ const form = page.locator("#email_registration_form");
+ await form.getByLabel("Email").fill(email);
+ await form.getByLabel("Display name").fill(displayName);
+ await form
.getByLabel("I am 18 or older and accept the safety rules")
.check();
- await page.getByRole("button", { name: "Create an account" }).click();
+ await form
+ .getByRole("button", { name: "Create account and email me a link" })
+ .click();
await expect(page).toHaveURL(/\/users\/log-in$/);
await expect(
page
@@ -352,7 +355,7 @@ export async function loginWithMagicLink(
await gotoWithTransientRetry(page, "/users/log-in");
const form = page.locator("#login_form_magic");
await form.getByLabel("Email").fill(email);
- await form.getByRole("button", { name: "Log in with email" }).click();
+ await form.getByRole("button", { name: "Email me a sign-in link" }).click();
await expect(
page.getByText(
"If your email is in our system, you will receive instructions for logging in shortly.",
diff --git a/lib/who_need_help/accounts.ex b/lib/who_need_help/accounts.ex
index afcf016..521e07d 100644
--- a/lib/who_need_help/accounts.ex
+++ b/lib/who_need_help/accounts.ex
@@ -7,7 +7,14 @@ defmodule WhoNeedHelp.Accounts do
alias WhoNeedHelp.Pagination
alias WhoNeedHelp.Repo
- alias WhoNeedHelp.Accounts.{Scope, SocialIdentity, User, UserToken, UserNotifier}
+ alias WhoNeedHelp.Accounts.{
+ AuthIdentity,
+ Scope,
+ SocialIdentity,
+ User,
+ UserNotifier,
+ UserToken
+ }
## Database getters
@@ -21,6 +28,14 @@ defmodule WhoNeedHelp.Accounts do
:user_id,
:inserted_at
]
+ @registration_key_atoms %{
+ "display_name" => :display_name,
+ "email" => :email,
+ "email_verified" => :email_verified,
+ "locale" => :locale,
+ "provider_uid" => :provider_uid,
+ "terms_accepted" => :terms_accepted
+ }
@doc """
Returns the deliberately small user projection used by public and
@@ -340,6 +355,260 @@ defmodule WhoNeedHelp.Accounts do
end
end
+ ## Authentication identities
+
+ def google_auth_connected?(%User{id: user_id}) do
+ Repo.exists?(
+ from identity in AuthIdentity,
+ where: identity.user_id == ^user_id and identity.provider == :google
+ )
+ end
+
+ def login_user_by_google(identity_attrs) do
+ with {:ok, identity_attrs} <- normalize_google_identity(identity_attrs) do
+ Repo.transact(fn ->
+ identity =
+ AuthIdentity
+ |> where(
+ [identity],
+ identity.provider == :google and
+ identity.provider_uid == ^identity_attrs.provider_uid
+ )
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ case identity do
+ nil ->
+ {:error, :not_linked}
+
+ %AuthIdentity{} = identity ->
+ with %User{} = user <-
+ User
+ |> where([user], user.id == ^identity.user_id)
+ |> lock("FOR UPDATE")
+ |> Repo.one(),
+ true <- user.moderation_status != :suspended,
+ {:ok, _identity} <-
+ identity
+ |> AuthIdentity.changeset(%{email: identity_attrs.email})
+ |> Repo.update() do
+ {:ok, user}
+ else
+ _missing_or_suspended -> {:error, :not_found}
+ end
+ end
+ end)
+ end
+ end
+
+ def register_user_by_google(identity_attrs, registration_attrs)
+ when is_map(registration_attrs) do
+ with {:ok, identity_attrs} <- normalize_google_identity(identity_attrs) do
+ Repo.transact(fn ->
+ identity =
+ AuthIdentity
+ |> where(
+ [identity],
+ identity.provider == :google and
+ identity.provider_uid == ^identity_attrs.provider_uid
+ )
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ case identity do
+ %AuthIdentity{} = identity ->
+ google_identity_user(identity, identity_attrs.email)
+
+ nil ->
+ register_google_identity(identity_attrs, registration_attrs)
+ end
+ end)
+ end
+ end
+
+ def register_user_by_google(_identity_attrs, _registration_attrs),
+ do: {:error, :invalid_registration}
+
+ def link_google_identity(%User{id: user_id}, identity_attrs) do
+ with {:ok, identity_attrs} <- normalize_google_identity(identity_attrs) do
+ Repo.transact(fn ->
+ user =
+ User
+ |> where([user], user.id == ^user_id)
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ cond do
+ is_nil(user) or user.moderation_status == :suspended ->
+ {:error, :not_found}
+
+ true ->
+ upsert_google_identity_for_user(user, identity_attrs)
+ end
+ end)
+ end
+ end
+
+ def unlink_google_identity(%User{id: user_id}) do
+ Repo.transact(fn ->
+ identity =
+ AuthIdentity
+ |> where(
+ [identity],
+ identity.provider == :google and identity.user_id == ^user_id
+ )
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ case identity do
+ %AuthIdentity{} = identity -> Repo.delete(identity)
+ nil -> {:error, :not_found}
+ end
+ end)
+ end
+
+ defp register_google_identity(identity_attrs, registration_attrs) do
+ existing_user =
+ User
+ |> where([user], user.email == ^identity_attrs.email)
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ if existing_user do
+ {:error, :email_already_registered}
+ else
+ attrs = %{
+ "email" => identity_attrs.email,
+ "display_name" => identity_attrs.display_name,
+ "locale" => registration_value(registration_attrs, "locale", "en"),
+ "terms_accepted" => registration_value(registration_attrs, "terms_accepted", false)
+ }
+
+ with {:ok, user} <- register_user(attrs),
+ {:ok, user} <- user |> User.confirm_changeset() |> Repo.update(),
+ {:ok, identity} <-
+ %AuthIdentity{}
+ |> AuthIdentity.changeset(%{
+ provider: :google,
+ provider_uid: identity_attrs.provider_uid,
+ email: identity_attrs.email,
+ user_id: user.id
+ })
+ |> Repo.insert() do
+ {:ok, {user, identity}}
+ end
+ end
+ end
+
+ defp google_identity_user(identity, current_email) do
+ user =
+ User
+ |> where([user], user.id == ^identity.user_id)
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ with %User{} = user <- user,
+ true <- user.moderation_status != :suspended,
+ {:ok, identity} <-
+ identity
+ |> AuthIdentity.changeset(%{email: current_email})
+ |> Repo.update() do
+ {:ok, {user, identity}}
+ else
+ _missing_or_suspended -> {:error, :not_found}
+ end
+ end
+
+ defp upsert_google_identity_for_user(user, identity_attrs) do
+ provider_identity =
+ AuthIdentity
+ |> where(
+ [identity],
+ identity.provider == :google and
+ identity.provider_uid == ^identity_attrs.provider_uid
+ )
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ user_identity =
+ AuthIdentity
+ |> where(
+ [identity],
+ identity.provider == :google and identity.user_id == ^user.id
+ )
+ |> lock("FOR UPDATE")
+ |> Repo.one()
+
+ cond do
+ provider_identity && provider_identity.user_id != user.id ->
+ {:error, :already_linked}
+
+ user_identity && user_identity.provider_uid != identity_attrs.provider_uid ->
+ {:error, :provider_already_linked}
+
+ identity = provider_identity || user_identity ->
+ identity
+ |> AuthIdentity.changeset(%{email: identity_attrs.email})
+ |> Repo.update()
+
+ true ->
+ %AuthIdentity{}
+ |> AuthIdentity.changeset(%{
+ provider: :google,
+ provider_uid: identity_attrs.provider_uid,
+ email: identity_attrs.email,
+ user_id: user.id
+ })
+ |> Repo.insert()
+ end
+ end
+
+ defp normalize_google_identity(identity_attrs) when is_map(identity_attrs) do
+ provider_uid = registration_value(identity_attrs, "provider_uid", nil)
+ email = registration_value(identity_attrs, "email", nil)
+ email_verified = registration_value(identity_attrs, "email_verified", false)
+ display_name = registration_value(identity_attrs, "display_name", nil)
+
+ cond do
+ email_verified != true ->
+ {:error, :email_not_verified}
+
+ not is_binary(provider_uid) or provider_uid == "" or byte_size(provider_uid) > 255 ->
+ {:error, :invalid_provider_identity}
+
+ not is_binary(email) or email == "" or byte_size(email) > 160 ->
+ {:error, :invalid_provider_identity}
+
+ not is_binary(display_name) or String.trim(display_name) == "" ->
+ {:error, :invalid_provider_identity}
+
+ true ->
+ {:ok,
+ %{
+ provider_uid: provider_uid,
+ email: email |> String.trim() |> String.downcase(),
+ email_verified: true,
+ display_name: display_name |> String.trim() |> String.slice(0, 80)
+ }}
+ end
+ end
+
+ defp normalize_google_identity(_identity_attrs),
+ do: {:error, :invalid_provider_identity}
+
+ defp registration_value(attrs, key, default) do
+ case Map.fetch(attrs, key) do
+ {:ok, value} ->
+ value
+
+ :error ->
+ case Map.fetch(@registration_key_atoms, key) do
+ {:ok, atom_key} -> Map.get(attrs, atom_key, default)
+ :error -> default
+ end
+ end
+ end
+
## Settings
@doc """
diff --git a/lib/who_need_help/accounts/auth_identity.ex b/lib/who_need_help/accounts/auth_identity.ex
new file mode 100644
index 0000000..db9bbb5
--- /dev/null
+++ b/lib/who_need_help/accounts/auth_identity.ex
@@ -0,0 +1,36 @@
+defmodule WhoNeedHelp.Accounts.AuthIdentity do
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ @primary_key {:id, :binary_id, autogenerate: true}
+ @foreign_key_type :binary_id
+
+ schema "auth_identities" do
+ field :provider, Ecto.Enum, values: [:google]
+ field :provider_uid, :string
+ field :email, :string
+ belongs_to :user, WhoNeedHelp.Accounts.User
+
+ timestamps(type: :utc_datetime)
+ end
+
+ def changeset(identity, attrs) do
+ identity
+ |> cast(attrs, [:provider, :provider_uid, :email, :user_id])
+ |> update_change(:email, &normalize_email/1)
+ |> validate_required([:provider, :provider_uid, :email, :user_id])
+ |> validate_inclusion(:provider, [:google])
+ |> validate_length(:provider_uid, min: 1, max: 255)
+ |> validate_format(:email, ~r/^[^@,;\s]+@[^@,;\s]+$/,
+ message: "must have the @ sign and no spaces"
+ )
+ |> validate_length(:email, max: 160)
+ |> unique_constraint([:provider, :provider_uid])
+ |> unique_constraint([:user_id, :provider])
+ end
+
+ defp normalize_email(email) when is_binary(email),
+ do: email |> String.trim() |> String.downcase()
+
+ defp normalize_email(email), do: email
+end
diff --git a/lib/who_need_help/external_boundary_drill.ex b/lib/who_need_help/external_boundary_drill.ex
index ef71f2a..131d801 100644
--- a/lib/who_need_help/external_boundary_drill.ex
+++ b/lib/who_need_help/external_boundary_drill.ex
@@ -17,9 +17,11 @@ defmodule WhoNeedHelp.ExternalBoundaryDrill do
alias WhoNeedHelp.Push.DisabledAdapter, as: PushDisabledAdapter
alias WhoNeedHelp.Push.HTTPAdapter, as: PushHTTPAdapter
alias WhoNeedHelp.Repo
+ alias WhoNeedHelp.GoogleAuth.AssentAdapter, as: GoogleAssentAdapter
alias WhoNeedHelp.SocialOAuth.AssentAdapter
@oauth_redirect_uri "http://boundary.local/auth/social/github/callback"
+ @google_redirect_uri "http://boundary.local/auth/google/callback"
@output_directory "/output"
@output_path "/output/summary.json"
@@ -36,6 +38,7 @@ defmodule WhoNeedHelp.ExternalBoundaryDrill do
summary = %{
status: "passed",
oauth: oauth_drill(base_url),
+ google_oidc: google_oidc_drill(base_url),
smtp: smtp_drill(base_url),
push: Map.put(protocol_push, :domain_workflow_integration, product_push.status),
push_product_integration: product_push
@@ -49,6 +52,118 @@ defmodule WhoNeedHelp.ExternalBoundaryDrill do
:ok
end
+ defp google_oidc_drill(base_url) do
+ assert!(GoogleAssentAdapter.enabled?(), "runtime Google OIDC config is disabled")
+
+ control!(base_url, "google", "success")
+ {success_session, success_params} = google_authorization!()
+
+ {:ok, identity} =
+ GoogleAssentAdapter.callback(
+ @google_redirect_uri,
+ success_params,
+ success_session
+ )
+
+ assert!(
+ identity.provider_uid == "google-local-subject",
+ "Google subject was not normalized"
+ )
+
+ assert!(
+ identity.email == "google-helper@example.invalid",
+ "Google email was not normalized"
+ )
+
+ assert!(
+ not Map.has_key?(identity, :access_token),
+ "Google access token escaped adapter boundary"
+ )
+
+ success_state = state!(base_url)["google"]
+ assert!(success_state["token_requests"] == 1, "Google token request was not observed")
+ assert!(success_state["jwks_requests"] == 1, "Google JWKS request was not observed")
+ assert!(success_state["consumed_codes"] == 1, "Google code was not consumed")
+
+ assert_error!(
+ GoogleAssentAdapter.callback(
+ @google_redirect_uri,
+ success_params,
+ success_session
+ ),
+ "Google authorization code replay unexpectedly succeeded"
+ )
+
+ replay_state = state!(base_url)["google"]
+ assert!(replay_state["token_requests"] == 2, "Google replay did not reach token endpoint")
+ assert!(replay_state["jwks_requests"] == 1, "Google replay reached the JWKS endpoint")
+
+ control!(base_url, "google", "success")
+ {mismatch_session, mismatch_params} = google_authorization!()
+
+ assert_error!(
+ GoogleAssentAdapter.callback(
+ @google_redirect_uri,
+ Map.put(mismatch_params, "state", "mismatched-state"),
+ mismatch_session
+ ),
+ "Google state mismatch unexpectedly succeeded"
+ )
+
+ mismatch_state = state!(base_url)["google"]
+ assert!(mismatch_state["token_requests"] == 0, "Google state mismatch reached token endpoint")
+
+ control!(base_url, "google", "nonce_mismatch")
+ {nonce_session, nonce_params} = google_authorization!()
+
+ assert_error!(
+ GoogleAssentAdapter.callback(
+ @google_redirect_uri,
+ nonce_params,
+ nonce_session
+ ),
+ "Google nonce mismatch unexpectedly succeeded"
+ )
+
+ nonce_state = state!(base_url)["google"]
+ assert!(nonce_state["token_requests"] == 1, "Google nonce test skipped token exchange")
+ assert!(nonce_state["jwks_requests"] == 1, "Google nonce test skipped signature verification")
+
+ control!(base_url, "google", "unverified_email")
+ {email_session, email_params} = google_authorization!()
+
+ assert!(
+ GoogleAssentAdapter.callback(
+ @google_redirect_uri,
+ email_params,
+ email_session
+ ) == {:error, :email_not_verified},
+ "Google unverified email was accepted"
+ )
+
+ %{
+ success: "passed",
+ state_mismatch_blocked_before_token: true,
+ one_time_code_replay_rejected: true,
+ nonce_mismatch_rejected_after_signature_verification: true,
+ unverified_email_rejected: true,
+ access_token_returned_to_application: false
+ }
+ end
+
+ defp google_authorization! do
+ {:ok, %{url: url, session_params: session_params}} =
+ GoogleAssentAdapter.authorize_url(@google_redirect_uri)
+
+ response = Req.get!(url, redirect: false, retry: false)
+ assert!(response.status == 302, "Google OIDC mock did not redirect")
+
+ [location] = Req.Response.get_header(response, "location")
+ params = location |> URI.parse() |> Map.fetch!(:query) |> URI.decode_query()
+
+ {session_params, params}
+ end
+
defp oauth_drill(base_url) do
assert!(AssentAdapter.enabled?(:github), "runtime GitHub OAuth config is disabled")
diff --git a/lib/who_need_help/google_auth.ex b/lib/who_need_help/google_auth.ex
new file mode 100644
index 0000000..1c1a3f4
--- /dev/null
+++ b/lib/who_need_help/google_auth.ex
@@ -0,0 +1,35 @@
+defmodule WhoNeedHelp.GoogleAuth do
+ @moduledoc """
+ Provider boundary for Google OpenID Connect authentication.
+
+ Provider access and ID tokens are consumed inside the adapter and are never
+ returned to controllers or persisted by the application.
+ """
+
+ @type identity :: %{
+ provider_uid: String.t(),
+ email: String.t(),
+ email_verified: true,
+ display_name: String.t()
+ }
+
+ @callback enabled?() :: boolean()
+ @callback authorize_url(String.t()) ::
+ {:ok, %{url: String.t(), session_params: map()}} | {:error, term()}
+ @callback callback(String.t(), map(), map()) :: {:ok, identity()} | {:error, term()}
+
+ def enabled?, do: adapter().enabled?()
+
+ def authorize_url(redirect_uri), do: adapter().authorize_url(redirect_uri)
+
+ def callback(redirect_uri, params, session_params),
+ do: adapter().callback(redirect_uri, params, session_params)
+
+ defp adapter do
+ Application.get_env(
+ :who_need_help,
+ :google_auth_adapter,
+ WhoNeedHelp.GoogleAuth.AssentAdapter
+ )
+ end
+end
diff --git a/lib/who_need_help/google_auth/assent_adapter.ex b/lib/who_need_help/google_auth/assent_adapter.ex
new file mode 100644
index 0000000..3418d1c
--- /dev/null
+++ b/lib/who_need_help/google_auth/assent_adapter.ex
@@ -0,0 +1,91 @@
+defmodule WhoNeedHelp.GoogleAuth.AssentAdapter do
+ @moduledoc false
+
+ @behaviour WhoNeedHelp.GoogleAuth
+
+ alias Assent.Strategy.Google
+
+ @impl true
+ def enabled?, do: not is_nil(provider_config())
+
+ @impl true
+ def authorize_url(redirect_uri) do
+ with {:ok, config} <- config(redirect_uri) do
+ config
+ |> Keyword.put(:nonce, random_url_token(32))
+ |> Keyword.put(:code_verifier, true)
+ |> Google.authorize_url()
+ end
+ end
+
+ @impl true
+ def callback(redirect_uri, params, session_params) do
+ with {:ok, config} <- config(redirect_uri),
+ {:ok, %{user: user}} <-
+ config
+ |> Keyword.put(:code_verifier, true)
+ |> Keyword.put(:session_params, session_params)
+ |> Google.callback(params) do
+ normalize_identity(user)
+ end
+ end
+
+ def normalize_identity(
+ %{
+ "sub" => provider_uid,
+ "email" => email,
+ "email_verified" => true
+ } = claims
+ )
+ when is_binary(provider_uid) and provider_uid != "" and is_binary(email) and email != "" do
+ normalized_email = email |> String.trim() |> String.downcase()
+
+ {:ok,
+ %{
+ provider_uid: provider_uid,
+ email: normalized_email,
+ email_verified: true,
+ display_name: display_name(normalized_email, Map.get(claims, "name"))
+ }}
+ end
+
+ def normalize_identity(%{"email_verified" => false}), do: {:error, :email_not_verified}
+ def normalize_identity(_claims), do: {:error, :invalid_provider_identity}
+
+ defp config(redirect_uri) do
+ case provider_config() do
+ nil -> {:error, :provider_disabled}
+ config -> {:ok, Keyword.put(config, :redirect_uri, redirect_uri)}
+ end
+ end
+
+ defp provider_config do
+ case Application.get_env(:who_need_help, :google_auth, []) do
+ config when is_list(config) and config != [] -> config
+ _disabled -> nil
+ end
+ end
+
+ defp display_name(email, name) when is_binary(name) do
+ case String.trim(name) do
+ "" -> fallback_display_name(email)
+ value -> String.slice(value, 0, 80)
+ end
+ end
+
+ defp display_name(email, _name), do: fallback_display_name(email)
+
+ defp fallback_display_name(email) do
+ candidate = email |> String.split("@", parts: 2) |> hd() |> String.trim()
+
+ if String.length(candidate) >= 2,
+ do: String.slice(candidate, 0, 80),
+ else: "Google user"
+ end
+
+ defp random_url_token(length) do
+ length
+ |> :crypto.strong_rand_bytes()
+ |> Base.url_encode64(padding: false)
+ 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 e59f131..234cb72 100644
--- a/lib/who_need_help_web/components/core_components.ex
+++ b/lib/who_need_help_web/components/core_components.ex
@@ -123,6 +123,59 @@ defmodule WhoNeedHelpWeb.CoreComponents do
end
end
+ @doc """
+ Renders the Google authentication action with the standard multicolor G.
+
+ The icon geometry and light button treatment follow Google's published
+ Sign in with Google branding guidance. The form itself owns the action and
+ CSRF token so the button remains a normal accessible submit control.
+ """
+ attr :label, :string, required: true
+ attr :disabled, :boolean, default: false
+ attr :rest, :global, include: ~w(id name value form)
+
+ def google_auth_button(assigns) do
+ ~H"""
+
+
+
+
+
+
+
+ {@label}
+
+ """
+ end
+
@doc """
Renders an input with label and error messages.
diff --git a/lib/who_need_help_web/controllers/google_auth_controller.ex b/lib/who_need_help_web/controllers/google_auth_controller.ex
new file mode 100644
index 0000000..8594cf3
--- /dev/null
+++ b/lib/who_need_help_web/controllers/google_auth_controller.ex
@@ -0,0 +1,278 @@
+defmodule WhoNeedHelpWeb.GoogleAuthController do
+ use WhoNeedHelpWeb, :controller
+
+ require Logger
+
+ alias WhoNeedHelp.{Accounts, GoogleAuth, Repo, Trust}
+ alias WhoNeedHelp.Trust.RateLimiter
+ alias WhoNeedHelpWeb.UserAuth
+
+ import WhoNeedHelpWeb.UserAuth, only: [require_sudo_mode: 2]
+
+ @session_key "google_auth_flow"
+ @supported_locales ~w(en uk ru)
+
+ plug :put_no_store
+ plug :require_sudo_mode when action in [:start_link, :disconnect]
+
+ def start_login(conn, _params) do
+ start_flow(conn, "login", %{}, ~p"/users/log-in")
+ end
+
+ def start_registration(conn, %{"google_registration" => params}) when is_map(params) do
+ locale = normalize_locale(params["locale"])
+ terms_accepted = params["terms_accepted"] in [true, "true", "on", "1"]
+
+ if terms_accepted do
+ start_flow(
+ conn,
+ "register",
+ %{"locale" => locale, "terms_accepted" => true},
+ ~p"/users/register"
+ )
+ else
+ conn
+ |> put_flash(
+ :error,
+ gettext("Confirm that you are 18 or older and accept the safety rules first.")
+ )
+ |> redirect(to: ~p"/users/register")
+ end
+ end
+
+ def start_registration(conn, _params) do
+ conn
+ |> put_flash(:error, gettext("The Google registration form is invalid."))
+ |> redirect(to: ~p"/users/register")
+ end
+
+ def start_link(conn, _params) do
+ user = conn.assigns.current_scope.user
+ start_flow(conn, "link", %{"user_id" => user.id}, ~p"/users/settings")
+ end
+
+ def disconnect(conn, _params) do
+ current_user = conn.assigns.current_scope.user
+
+ case unlink_google_identity_and_audit(current_user) do
+ {:ok, _identity} ->
+ conn
+ |> put_flash(:info, gettext("Google sign-in disconnected."))
+ |> redirect(to: ~p"/users/settings")
+
+ {:error, :not_found} ->
+ conn
+ |> put_flash(:error, gettext("No Google account is connected."))
+ |> redirect(to: ~p"/users/settings")
+
+ {:error, _reason} ->
+ google_account_unavailable(conn, ~p"/users/settings")
+ end
+ end
+
+ def callback(conn, params) do
+ flow = get_session(conn, @session_key)
+ conn = delete_session(conn, @session_key)
+
+ with %{"flow" => flow_name, "session_params" => session_params} <- flow,
+ true <- flow_name in ["login", "register", "link"],
+ {:ok, identity_attrs} <-
+ GoogleAuth.callback(callback_url(conn), params, session_params) do
+ finish_flow(conn, flow_name, flow, identity_attrs)
+ else
+ nil ->
+ callback_error(
+ conn,
+ flow,
+ gettext("Google sign-in session expired. Please start again.")
+ )
+
+ false ->
+ callback_error(conn, flow, gettext("Google sign-in session is invalid."))
+
+ {:error, :email_not_verified} ->
+ callback_error(
+ conn,
+ flow,
+ gettext("Google did not provide a verified email address.")
+ )
+
+ {:error, error} ->
+ Logger.warning("Google OAuth callback failed (#{error_name(error)})")
+ callback_error(conn, flow, gettext("Google sign-in failed. Please try again."))
+ end
+ end
+
+ defp start_flow(conn, flow, extra, failure_path) do
+ if GoogleAuth.enabled?() do
+ case GoogleAuth.authorize_url(callback_url(conn)) do
+ {:ok, %{url: url, session_params: session_params}} ->
+ session =
+ extra
+ |> Map.put("flow", flow)
+ |> Map.put("session_params", session_params)
+
+ conn
+ |> put_session(@session_key, session)
+ |> redirect(external: url)
+
+ {:error, error} ->
+ Logger.warning("Unable to start Google OAuth (#{error_name(error)})")
+
+ conn
+ |> put_flash(:error, gettext("Google sign-in could not be started."))
+ |> redirect(to: failure_path)
+ end
+ else
+ conn
+ |> put_flash(:error, gettext("Google sign-in is not configured yet."))
+ |> redirect(to: failure_path)
+ end
+ end
+
+ defp finish_flow(conn, "login", _flow, identity_attrs) do
+ case Accounts.login_user_by_google(identity_attrs) do
+ {:ok, user} ->
+ conn
+ |> put_flash(:info, gettext("Welcome back!"))
+ |> UserAuth.log_in_user(user)
+
+ {:error, :not_linked} ->
+ conn
+ |> put_flash(
+ :error,
+ gettext(
+ "This Google account is not connected yet. Create an account or sign in by email and connect Google in account settings."
+ )
+ )
+ |> redirect(to: ~p"/users/log-in")
+
+ {:error, _reason} ->
+ google_account_unavailable(conn, ~p"/users/log-in")
+ end
+ end
+
+ defp finish_flow(conn, "register", flow, identity_attrs) do
+ with {:ok, _limit} <- RateLimiter.check(:registration_email, identity_attrs.email),
+ {:ok, {user, _identity}} <-
+ Accounts.register_user_by_google(identity_attrs, %{
+ "locale" => flow["locale"],
+ "terms_accepted" => flow["terms_accepted"]
+ }) do
+ conn
+ |> put_flash(:info, gettext("Your account was created with Google."))
+ |> UserAuth.log_in_user(user)
+ else
+ {:error, :email_already_registered} ->
+ conn
+ |> put_flash(
+ :error,
+ gettext(
+ "An account with this email already exists. Sign in by email or password, then connect Google in account settings."
+ )
+ )
+ |> redirect(to: ~p"/users/log-in")
+
+ {:error, :rate_limited} ->
+ conn
+ |> put_flash(
+ :error,
+ gettext("Too many registration attempts in the configured time window.")
+ )
+ |> redirect(to: ~p"/users/register")
+
+ {:error, _reason} ->
+ google_account_unavailable(conn, ~p"/users/register")
+ end
+ end
+
+ defp finish_flow(conn, "link", flow, identity_attrs) do
+ current_user = get_in(conn.assigns, [:current_scope, Access.key(:user)])
+
+ if current_user && flow["user_id"] == current_user.id &&
+ Accounts.sudo_mode?(current_user, -10) do
+ case link_google_identity_and_audit(current_user, identity_attrs) do
+ {:ok, _identity} ->
+ conn
+ |> put_flash(:info, gettext("Google sign-in connected."))
+ |> redirect(to: ~p"/users/settings")
+
+ {:error, :already_linked} ->
+ conn
+ |> put_flash(:error, gettext("That Google account belongs to another local account."))
+ |> redirect(to: ~p"/users/settings")
+
+ {:error, :provider_already_linked} ->
+ conn
+ |> put_flash(:error, gettext("A different Google account is already connected."))
+ |> redirect(to: ~p"/users/settings")
+
+ {:error, _reason} ->
+ google_account_unavailable(conn, ~p"/users/settings")
+ end
+ else
+ conn
+ |> put_flash(:error, gettext("Google account connection session is invalid."))
+ |> redirect(to: ~p"/users/log-in")
+ end
+ end
+
+ defp link_google_identity_and_audit(current_user, identity_attrs) do
+ Repo.transact(fn ->
+ with {:ok, identity} <- Accounts.link_google_identity(current_user, identity_attrs),
+ {:ok, _audit} <-
+ Trust.audit(
+ current_user.id,
+ "auth_identity.connected",
+ "auth_identity",
+ identity.id,
+ %{"provider" => "google"}
+ ) do
+ {:ok, identity}
+ end
+ end)
+ end
+
+ defp unlink_google_identity_and_audit(current_user) do
+ Repo.transact(fn ->
+ with {:ok, identity} <- Accounts.unlink_google_identity(current_user),
+ {:ok, _audit} <-
+ Trust.audit(
+ current_user.id,
+ "auth_identity.disconnected",
+ "auth_identity",
+ identity.id,
+ %{"provider" => "google"}
+ ) do
+ {:ok, identity}
+ end
+ end)
+ end
+
+ defp callback_error(conn, flow, message) do
+ conn
+ |> put_flash(:error, message)
+ |> redirect(to: failure_path(flow))
+ end
+
+ defp google_account_unavailable(conn, path) do
+ conn
+ |> put_flash(:error, gettext("This Google account cannot be used right now."))
+ |> redirect(to: path)
+ end
+
+ defp failure_path(%{"flow" => "register"}), do: ~p"/users/register"
+ defp failure_path(%{"flow" => "link"}), do: ~p"/users/settings"
+ defp failure_path(_flow), do: ~p"/users/log-in"
+
+ defp callback_url(conn), do: url(conn, ~p"/auth/google/callback")
+
+ defp normalize_locale(locale) when locale in @supported_locales, do: locale
+ defp normalize_locale(_locale), do: "en"
+
+ defp error_name(error) when is_atom(error), do: Atom.to_string(error)
+ defp error_name(%{__struct__: module}), do: inspect(module)
+ defp error_name(_error), do: "unknown_error"
+
+ 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_registration_controller.ex b/lib/who_need_help_web/controllers/user_registration_controller.ex
index 67ec477..c57ab6a 100644
--- a/lib/who_need_help_web/controllers/user_registration_controller.ex
+++ b/lib/who_need_help_web/controllers/user_registration_controller.ex
@@ -3,7 +3,7 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
require Logger
- alias WhoNeedHelp.Accounts
+ alias WhoNeedHelp.{Accounts, GoogleAuth}
alias WhoNeedHelp.Accounts.User
alias WhoNeedHelp.Trust.RateLimiter
@@ -11,7 +11,7 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
def new(conn, _params) do
changeset = Accounts.change_user_registration(%User{}, %{}, validate_unique: false)
- render(conn, :new, changeset: changeset)
+ render_registration(conn, changeset)
end
def create(conn, %{"user" => user_params}) when is_map(user_params) do
@@ -37,7 +37,7 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
registration_response(conn)
else
- render(conn, :new, changeset: changeset)
+ render_registration(conn, changeset)
end
end
else
@@ -48,9 +48,7 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
:error,
gettext("Too many registration attempts in the configured time window.")
)
- |> render(:new,
- changeset: Accounts.change_user_registration(%User{}, user_params)
- )
+ |> render_registration(Accounts.change_user_registration(%User{}, user_params))
end
end
@@ -58,8 +56,8 @@ defmodule WhoNeedHelpWeb.UserRegistrationController 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)
+ |> render_registration(
+ Accounts.change_user_registration(%User{}, %{}, validate_unique: false)
)
end
@@ -124,5 +122,12 @@ defmodule WhoNeedHelpWeb.UserRegistrationController do
end)
end
+ defp render_registration(conn, changeset) do
+ render(conn, :new,
+ changeset: changeset,
+ google_auth_enabled: GoogleAuth.enabled?()
+ )
+ 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_registration_html/new.html.heex b/lib/who_need_help_web/controllers/user_registration_html/new.html.heex
index a02fb5a..ab05a9f 100644
--- a/lib/who_need_help_web/controllers/user_registration_html/new.html.heex
+++ b/lib/who_need_help_web/controllers/user_registration_html/new.html.heex
@@ -1,5 +1,5 @@
-
+
<.header>
{gettext("Register for an account")}
@@ -13,7 +13,49 @@
- <.form :let={f} for={@changeset} action={~p"/users/register"}>
+
+ {gettext(
+ "A password is not required. We will email a one-time confirmation link. You can add a password later in account settings."
+ )}
+
+
+ <.form
+ for={%{}}
+ as={:google_registration}
+ action={~p"/auth/google/register"}
+ id="google_registration_form"
+ class="space-y-3"
+ >
+
+ <.input
+ type="checkbox"
+ id="google_registration_terms"
+ name="google_registration[terms_accepted]"
+ value="false"
+ label={gettext("I am 18 or older and accept the safety rules")}
+ required
+ />
+ <.google_auth_button
+ label={gettext("Sign up with Google")}
+ disabled={!@google_auth_enabled}
+ />
+
+ {gettext("Google sign-in will be available after the operator configures it.")}
+
+
+
+
{gettext("or sign up with email")}
+
+ <.form
+ :let={f}
+ for={@changeset}
+ action={~p"/users/register"}
+ id="email_registration_form"
+ >
- <.button phx-disable-with={gettext("Creating account...")} class="btn btn-primary w-full">
- {gettext("Create an account")}
+ <.button phx-disable-with={gettext("Sending link...")} class="btn btn-primary w-full">
+ {gettext("Create account and email me a link")}
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 ae41ce4..b6821ae 100644
--- a/lib/who_need_help_web/controllers/user_session_controller.ex
+++ b/lib/who_need_help_web/controllers/user_session_controller.ex
@@ -3,7 +3,7 @@ defmodule WhoNeedHelpWeb.UserSessionController do
require Logger
- alias WhoNeedHelp.Accounts
+ alias WhoNeedHelp.{Accounts, GoogleAuth}
alias WhoNeedHelp.Trust.RateLimiter
alias WhoNeedHelpWeb.UserAuth
@@ -126,11 +126,12 @@ defmodule WhoNeedHelpWeb.UserSessionController do
end
defp assign_magic_link_form(conn, _opts) do
- assign(
- conn,
+ conn
+ |> assign(
:magic_link_form,
Phoenix.Component.to_form(%{"token" => ""}, as: "user")
)
+ |> assign(:google_auth_enabled, GoogleAuth.enabled?())
end
defp put_no_store(conn, _opts), do: put_resp_header(conn, "cache-control", "no-store")
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 93a19b1..2eae3a8 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
@@ -63,6 +63,24 @@
+ <.form
+ :if={!@current_scope}
+ for={%{}}
+ as={:google_login}
+ action={~p"/auth/google/login"}
+ id="google_login_form"
+ >
+ <.google_auth_button
+ label={gettext("Sign in with Google")}
+ disabled={!@google_auth_enabled}
+ />
+
+ {gettext("Google sign-in will be available after the operator configures it.")}
+
+
+
+
{gettext("or use email")}
+
<.form :let={f} for={@form} as={:user} id="login_form_magic" action={~p"/users/log-in"}>
<.input
readonly={!!@current_scope}
@@ -74,37 +92,55 @@
required
phx-mounted={JS.focus()}
/>
+
+ {gettext("We will send a one-time sign-in link. No password is required.")}
+
<.button class="btn btn-primary w-full">
- {gettext("Log in with email")}
→
+ {gettext("Email me a sign-in link")}
→
-
{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")}
-
-
+
+
+ {gettext("Use a password instead")}
+
+
+
+ {gettext("This works only if you previously added a password in account settings.")}
+
+ <.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"
+ required
+ />
+ <.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 7869039..3f95026 100644
--- a/lib/who_need_help_web/controllers/user_settings_controller.ex
+++ b/lib/who_need_help_web/controllers/user_settings_controller.ex
@@ -3,7 +3,7 @@ defmodule WhoNeedHelpWeb.UserSettingsController do
require Logger
- alias WhoNeedHelp.Accounts
+ alias WhoNeedHelp.{Accounts, GoogleAuth}
alias WhoNeedHelp.Trust.RateLimiter
alias WhoNeedHelpWeb.UserAuth
@@ -103,6 +103,8 @@ defmodule WhoNeedHelpWeb.UserSettingsController do
conn
|> assign(:email_changeset, Accounts.change_user_email(user))
|> assign(:password_changeset, Accounts.change_user_password(user))
+ |> assign(:google_auth_enabled, GoogleAuth.enabled?())
+ |> assign(:google_auth_connected, Accounts.google_auth_connected?(user))
end
defp deliver_email_change_instructions(conn, user, changeset) do
diff --git a/lib/who_need_help_web/controllers/user_settings_html/edit.html.heex b/lib/who_need_help_web/controllers/user_settings_html/edit.html.heex
index d2efc09..849e466 100644
--- a/lib/who_need_help_web/controllers/user_settings_html/edit.html.heex
+++ b/lib/who_need_help_web/controllers/user_settings_html/edit.html.heex
@@ -25,6 +25,55 @@
+
+ {gettext("Google sign-in")}
+
+ <%= if @google_auth_connected do %>
+ {gettext("A Google account is connected. You can use it to sign in.")}
+ <% else %>
+ {gettext(
+ "Connect Google only after signing in here. We never merge accounts automatically by matching email addresses."
+ )}
+ <% end %>
+
+
+ <.form
+ :if={!@google_auth_connected}
+ for={%{}}
+ as={:google_link}
+ action={~p"/auth/google/link"}
+ id="google_link_form"
+ class="mt-4"
+ >
+ <.google_auth_button
+ label={gettext("Connect Google account")}
+ disabled={!@google_auth_enabled}
+ />
+
+ {gettext("Google sign-in will be available after the operator configures it.")}
+
+
+
+ <.form
+ :if={@google_auth_connected}
+ for={%{}}
+ action={~p"/auth/google/link"}
+ method="delete"
+ id="google_unlink_form"
+ class="mt-4"
+ >
+ <.button
+ variant="primary"
+ class="btn-soft"
+ data-confirm={gettext("Disconnect Google sign-in from this account?")}
+ >
+ {gettext("Disconnect Google")}
+
+
+
+
+
+
<.form :let={f} for={@password_changeset} action={~p"/users/settings"} id="update_password">
bytes:
return json.dumps(value, separators=(",", ":"), sort_keys=True).encode()
+def base64url(value: bytes) -> str:
+ return base64.urlsafe_b64encode(value).rstrip(b"=").decode()
+
+
+def unsigned_bytes(value: int) -> bytes:
+ return value.to_bytes((value.bit_length() + 7) // 8, "big")
+
+
+def google_id_token(code: dict[str, Any], mode: str) -> str:
+ now = int(time.time())
+ header = {"alg": "RS256", "kid": GOOGLE_KEY_ID, "typ": "JWT"}
+ claims = {
+ "iss": GOOGLE_ISSUER,
+ "sub": "google-local-subject",
+ "aud": OAUTH_CLIENT_ID,
+ "exp": now + 300,
+ "iat": now,
+ "nonce": (
+ "invalid-nonce" if mode == "nonce_mismatch" else code["nonce"]
+ ),
+ "email": "google-helper@example.invalid",
+ "email_verified": mode != "unverified_email",
+ "name": "Google Local Helper",
+ }
+ signing_input = (
+ f"{base64url(json_bytes(header))}.{base64url(json_bytes(claims))}"
+ ).encode()
+ digest_info = bytes.fromhex("3031300d060960864801650304020105000420")
+ digest_info += hashlib.sha256(signing_input).digest()
+ padding = b"\xff" * (256 - len(digest_info) - 3)
+ encoded_message = b"\x00\x01" + padding + b"\x00" + digest_info
+ signature = pow(
+ int.from_bytes(encoded_message, "big"),
+ GOOGLE_RSA_PRIVATE_EXPONENT,
+ GOOGLE_RSA_MODULUS,
+ ).to_bytes(256, "big")
+ return f"{signing_input.decode()}.{base64url(signature)}"
+
class BoundaryHTTPServer(ThreadingHTTPServer):
daemon_threads = True
@@ -151,6 +239,12 @@ class BoundaryHTTPHandler(BaseHTTPRequestHandler):
self.oauth_authorize(parsed)
elif parsed.path == "/oauth/user":
self.oauth_user()
+ elif parsed.path == "/.well-known/openid-configuration":
+ self.google_discovery()
+ elif parsed.path == "/google/authorize":
+ self.google_authorize(parsed)
+ elif parsed.path == "/google/jwks":
+ self.google_jwks()
else:
self.send_json(404, {"error": "not_found"})
@@ -167,6 +261,8 @@ class BoundaryHTTPHandler(BaseHTTPRequestHandler):
self.control(body)
elif parsed.path == "/oauth/token":
self.oauth_token(body)
+ elif parsed.path == "/google/token":
+ self.google_token(body)
elif parsed.path == "/push":
self.push(body)
else:
@@ -314,6 +410,139 @@ class BoundaryHTTPHandler(BaseHTTPRequestHandler):
},
)
+ def google_discovery(self) -> None:
+ with STATE.lock:
+ STATE.google_discovery_requests += 1
+
+ self.send_json(
+ 200,
+ {
+ "issuer": GOOGLE_ISSUER,
+ "authorization_endpoint": f"{GOOGLE_ISSUER}/google/authorize",
+ "token_endpoint": f"{GOOGLE_ISSUER}/google/token",
+ "jwks_uri": f"{GOOGLE_ISSUER}/google/jwks",
+ "token_endpoint_auth_methods_supported": ["client_secret_post"],
+ },
+ )
+
+ def google_authorize(self, parsed: urllib.parse.SplitResult) -> None:
+ params = urllib.parse.parse_qs(parsed.query)
+ required = {
+ "client_id",
+ "redirect_uri",
+ "state",
+ "nonce",
+ "scope",
+ "code_challenge",
+ "code_challenge_method",
+ }
+
+ if (
+ not required.issubset(params)
+ or params["client_id"][0] != OAUTH_CLIENT_ID
+ or params["code_challenge_method"][0] != "S256"
+ or set(params["scope"][0].split()) != {"openid", "email", "profile"}
+ ):
+ self.send_json(400, {"error": "invalid_google_authorization_request"})
+ return
+
+ redirect_uri = params["redirect_uri"][0]
+ state = params["state"][0]
+
+ with STATE.lock:
+ STATE.google_authorize_requests += 1
+ STATE.google_code_counter += 1
+ code = f"google-boundary-code-{STATE.google_code_counter}"
+ STATE.google_codes[code] = {
+ "challenge": params["code_challenge"][0],
+ "redirect_uri": redirect_uri,
+ "nonce": params["nonce"][0],
+ "used": False,
+ }
+
+ location = (
+ f"{redirect_uri}?{urllib.parse.urlencode({'code': code, 'state': state})}"
+ )
+ self.send_response(302)
+ self.send_header("location", location)
+ self.send_header("content-length", "0")
+ self.end_headers()
+
+ def google_token(self, body: bytes) -> None:
+ params = {
+ key: values[0]
+ for key, values in urllib.parse.parse_qs(body.decode()).items()
+ }
+
+ with STATE.lock:
+ STATE.google_token_requests += 1
+ mode = STATE.google_mode
+ code = STATE.google_codes.get(params.get("code", ""))
+
+ verifier = params.get("code_verifier", "")
+ challenge = base64url(hashlib.sha256(verifier.encode()).digest())
+ checks = {
+ "client_id": params.get("client_id") == OAUTH_CLIENT_ID,
+ "client_secret": params.get("client_secret") == OAUTH_CLIENT_SECRET,
+ "grant_type": params.get("grant_type") == "authorization_code",
+ "known_code": code is not None,
+ "unused_code": code is not None and not code["used"],
+ "redirect_uri": (
+ code is not None and code["redirect_uri"] == params.get("redirect_uri")
+ ),
+ "pkce": code is not None and code["challenge"] == challenge,
+ }
+ valid = all(checks.values())
+
+ if not valid:
+ failed_checks = sorted(name for name, passed in checks.items() if not passed)
+ if not checks["pkce"] and code is not None:
+ failed_checks.append(
+ "pkce_lengths_"
+ f"{len(verifier)}_{len(code['challenge'])}_{len(challenge)}"
+ )
+ self.send_json(
+ 400,
+ {
+ "error": "invalid_grant",
+ "error_description": ",".join(failed_checks),
+ },
+ )
+ return
+
+ with STATE.lock:
+ code["used"] = True
+
+ self.send_json(
+ 200,
+ {
+ "access_token": "google-boundary-access-token",
+ "id_token": google_id_token(code, mode),
+ "scope": "openid email profile",
+ "token_type": "Bearer",
+ },
+ )
+
+ def google_jwks(self) -> None:
+ with STATE.lock:
+ STATE.google_jwks_requests += 1
+
+ self.send_json(
+ 200,
+ {
+ "keys": [
+ {
+ "alg": "RS256",
+ "e": base64url(unsigned_bytes(GOOGLE_RSA_EXPONENT)),
+ "kid": GOOGLE_KEY_ID,
+ "kty": "RSA",
+ "n": base64url(unsigned_bytes(GOOGLE_RSA_MODULUS)),
+ "use": "sig",
+ }
+ ]
+ },
+ )
+
def push(self, body: bytes) -> None:
try:
notification = json.loads(body)
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index c66ac0b..0c4b56a 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -17,7 +17,7 @@ msgstr ""
msgid "Account settings"
msgstr ""
-#: lib/who_need_help_web/components/core_components.ex:393
+#: lib/who_need_help_web/components/core_components.ex:446
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
@@ -25,7 +25,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:23
#, elixir-autogen, elixir-format
msgid "Ask for help"
msgstr ""
@@ -108,7 +108,7 @@ msgstr ""
msgid "close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:363
+#: lib/who_need_help_web/live/activity_live/show.ex:367
#, elixir-autogen, elixir-format
msgid "%{approved}/%{capacity} approved"
msgstr ""
@@ -118,37 +118,37 @@ msgstr ""
msgid "%{count} community votes"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:745
+#: lib/who_need_help_web/live/request_live/show.ex:750
#, elixir-autogen, elixir-format
msgid "%{count} unique people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:829
+#: lib/who_need_help_web/live/request_live/show.ex:842
#, elixir-autogen, elixir-format
msgid "1 — unsafe"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:828
+#: lib/who_need_help_web/live/request_live/show.ex:841
#, elixir-autogen, elixir-format
msgid "2 — poor"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:827
+#: lib/who_need_help_web/live/request_live/show.ex:840
#, elixir-autogen, elixir-format
msgid "3 — okay"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:826
+#: lib/who_need_help_web/live/request_live/show.ex:839
#, elixir-autogen, elixir-format
msgid "4 — good"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:825
+#: lib/who_need_help_web/live/request_live/show.ex:838
#, elixir-autogen, elixir-format
msgid "5 — excellent"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:118
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:120
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
@@ -158,12 +158,12 @@ msgstr ""
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:859
+#: lib/who_need_help_web/live/request_live/show.ex:872
#, 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:633
+#: lib/who_need_help_web/live/request_live/show.ex:638
#, elixir-autogen, elixir-format
msgid "Accepted"
msgstr ""
@@ -184,7 +184,7 @@ msgstr ""
msgid "Accounts"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:855
+#: lib/who_need_help_web/live/request_live/show.ex:868
#, elixir-autogen, elixir-format
msgid "Action center"
msgstr ""
@@ -206,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:163
+#: lib/who_need_help_web/live/activity_live/new.ex:168
#, elixir-autogen, elixir-format
msgid "Activity details"
msgstr ""
@@ -217,12 +217,12 @@ msgid "Activity hidden."
msgstr ""
#: lib/who_need_help_web/live/activity_live/show.ex:18
-#: lib/who_need_help_web/live/activity_live/show.ex:177
+#: lib/who_need_help_web/live/activity_live/show.ex:181
#, elixir-autogen, elixir-format
msgid "Activity is unavailable."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:36
+#: lib/who_need_help_web/live/activity_live/new.ex:41
#, elixir-autogen, elixir-format
msgid "Activity published. You approve every participant."
msgstr ""
@@ -233,7 +233,7 @@ msgid "Activity restored."
msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:210
-#: lib/who_need_help_web/live/activity_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Activity type"
msgstr ""
@@ -264,7 +264,7 @@ msgstr ""
msgid "Already registered?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:603
+#: lib/who_need_help_web/live/request_live/show.ex:608
#, elixir-autogen, elixir-format
msgid "Another helper already accepted this request."
msgstr ""
@@ -274,60 +274,60 @@ msgstr ""
msgid "Any urgency"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:524
+#: lib/who_need_help_web/live/activity_live/show.ex:528
#, elixir-autogen, elixir-format
msgid "Approval pending"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:574
+#: lib/who_need_help_web/live/activity_live/show.ex:581
#, elixir-autogen, elixir-format
msgid "Approve"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:435
+#: lib/who_need_help_web/live/activity_live/show.ex:439
#, elixir-autogen, elixir-format
msgid "Approved group chat"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:600
+#: lib/who_need_help_web/live/activity_live/show.ex:607
#, 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:258
-#: lib/who_need_help_web/live/request_live/new.ex:306
+#: lib/who_need_help_web/live/activity_live/new.ex:263
+#: lib/who_need_help_web/live/request_live/new.ex:311
#, elixir-autogen, elixir-format
msgid "Approximate area"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:250
+#: lib/who_need_help_web/live/activity_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Approximate public meeting area"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:167
-#: lib/who_need_help_web/live/request_live/show.ex:639
+#: lib/who_need_help_web/live/request_live/show.ex:644
#, elixir-autogen, elixir-format
msgid "Approximate publicly"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:314
+#: lib/who_need_help_web/live/request_live/new.ex:319
#, elixir-autogen, elixir-format
msgid "Approximate publicly (recommended)"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:702
+#: lib/who_need_help_web/live/request_live/show.ex:707
#, elixir-autogen, elixir-format
msgid "Area:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:154
+#: lib/who_need_help_web/live/request_live/new.ex:159
#, elixir-autogen, elixir-format
msgid "Ask for urgent help"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:92
+#: lib/who_need_help_web/live/category_proposal_live.ex:98
#, elixir-autogen, elixir-format
msgid "Bicycle puncture"
msgstr ""
@@ -342,12 +342,12 @@ msgstr ""
msgid "Block and report"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:641
+#: lib/who_need_help_web/live/activity_live/show.ex:648
#, elixir-autogen, elixir-format
msgid "Block organizer"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1062
+#: lib/who_need_help_web/live/request_live/show.ex:1075
#, elixir-autogen, elixir-format
msgid "Block this user"
msgstr ""
@@ -362,7 +362,7 @@ msgstr ""
msgid "Both confirm and the helper enters a one-time code."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:118
+#: lib/who_need_help_web/live/request_live/new.ex:123
#, elixir-autogen, elixir-format
msgid "Briefly describe the help you need"
msgstr ""
@@ -382,33 +382,33 @@ msgstr ""
msgid "COMMUNITY TRUST"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:80
+#: lib/who_need_help_web/live/category_proposal_live.ex:81
#, elixir-autogen, elixir-format
msgid "COMMUNITY-DRIVEN TAXONOMY"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:594
+#: lib/who_need_help_web/live/activity_live/show.ex:601
#, elixir-autogen, elixir-format
msgid "Cancel activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:873
-#: lib/who_need_help_web/live/request_live/show.ex:967
+#: lib/who_need_help_web/live/request_live/show.ex:886
+#: lib/who_need_help_web/live/request_live/show.ex:980
#, elixir-autogen, elixir-format
msgid "Cancel request"
msgstr ""
#: 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/activity_live/show.ex:344
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:641
#, elixir-autogen, elixir-format
msgid "Cancelled"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:225
-#: lib/who_need_help_web/live/request_live/new.ex:180
+#: lib/who_need_help_web/live/request_live/new.ex:185
#, elixir-autogen, elixir-format
msgid "Category"
msgstr ""
@@ -418,13 +418,13 @@ msgstr ""
msgid "Category created and proposal approved."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:211
-#: lib/who_need_help_web/live/request_live/show.ex:691
+#: lib/who_need_help_web/live/request_live/new.ex:216
+#: lib/who_need_help_web/live/request_live/show.ex:696
#, 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/category_proposal_live.ex:67
#: lib/who_need_help_web/live/moderation_live.ex:466
#, elixir-autogen, elixir-format
msgid "Category proposals"
@@ -441,7 +441,7 @@ msgid "Change role"
msgstr ""
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:21
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:47
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:107
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr ""
@@ -451,31 +451,31 @@ msgstr ""
msgid "Choose a category, urgency, and safe location visibility."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:140
+#: lib/who_need_help_web/live/activity_live/new.ex:145
#, elixir-autogen, elixir-format
msgid "Choose activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:181
+#: lib/who_need_help_web/live/request_live/new.ex:186
#, elixir-autogen, elixir-format
msgid "Choose category"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:180
+#: lib/who_need_help_web/live/activity_live/new.ex:207
+#: lib/who_need_help_web/live/request_live/new.ex:228
+#: lib/who_need_help_web/live/request_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Choose…"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:251
+#: lib/who_need_help_web/live/activity_live/new.ex:256
#, elixir-autogen, elixir-format
msgid "City centre, near the main entrance"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:654
-#: lib/who_need_help_web/live/request_live/show.ex:1078
+#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/request_live/show.ex:1096
#, elixir-autogen, elixir-format
msgid "Clear"
msgstr ""
@@ -485,7 +485,7 @@ msgstr ""
msgid "Close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:220
+#: lib/who_need_help_web/live/activity_live/new.ex:225
#, elixir-autogen, elixir-format
msgid "Coffee and conversation"
msgstr ""
@@ -495,7 +495,7 @@ msgstr ""
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:835
+#: lib/who_need_help_web/live/request_live/show.ex:848
#, elixir-autogen, elixir-format
msgid "Comment (optional)"
msgstr ""
@@ -506,19 +506,19 @@ msgid "Community helpers"
msgstr ""
#: 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/activity_live/show.ex:396
+#: lib/who_need_help_web/live/activity_live/show.ex:553
+#: lib/who_need_help_web/live/activity_live/show.ex:610
#: 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:167
-#: lib/who_need_help_web/live/activity_live/show.ex:339
+#: lib/who_need_help_web/live/activity_live/show.ex:343
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:640
#, elixir-autogen, elixir-format
msgid "Completed"
msgstr ""
@@ -533,24 +533,24 @@ msgstr ""
msgid "Confirm and stay logged in"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:947
+#: lib/who_need_help_web/live/request_live/show.ex:960
#, elixir-autogen, elixir-format
msgid "Confirm my part is complete"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:42
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:102
#, elixir-autogen, elixir-format
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:611
+#: lib/who_need_help_web/live/category_proposal_live.ex:52
+#: lib/who_need_help_web/live/request_live/show.ex:616
#, 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:100
-#: lib/who_need_help_web/live/request_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:105
+#: lib/who_need_help_web/live/request_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Confirm your email and ensure your account is active before publishing."
msgstr ""
@@ -573,17 +573,12 @@ msgid "Create a clear plan in a public place."
msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:188
-#: lib/who_need_help_web/live/activity_live/new.ex:11
+#: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format
msgid "Create activity"
msgstr ""
-#: 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:115
+#: lib/who_need_help_web/live/activity_live/new.ex:120
#, elixir-autogen, elixir-format
msgid "Create an activity"
msgstr ""
@@ -598,18 +593,13 @@ msgstr ""
msgid "Create urgent request"
msgstr ""
-#: 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:663
+#: lib/who_need_help_web/live/activity_live/show.ex:675
#, elixir-autogen, elixir-format
msgid "Dangerous plan"
msgstr ""
#: lib/who_need_help_web/live/moderation_live.ex:248
-#: lib/who_need_help_web/live/request_live/show.ex:1086
+#: lib/who_need_help_web/live/request_live/show.ex:1104
#, elixir-autogen, elixir-format
msgid "Dangerous request"
msgstr ""
@@ -629,7 +619,7 @@ msgstr ""
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:581
+#: lib/who_need_help_web/live/activity_live/show.ex:588
#, elixir-autogen, elixir-format
msgid "Decline"
msgstr ""
@@ -639,7 +629,7 @@ msgstr ""
msgid "Default location visibility"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:227
+#: lib/who_need_help_web/live/activity_live/new.ex:232
#, elixir-autogen, elixir-format
msgid "Describe the public plan, approximate duration, and who might enjoy it."
msgstr ""
@@ -651,7 +641,7 @@ msgstr ""
msgid "Dismissed"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:71
#: lib/who_need_help_web/live/profile_live.ex:149
#, elixir-autogen, elixir-format
msgid "Display name"
@@ -667,14 +657,14 @@ msgstr ""
msgid "Don't have an account?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:815
+#: lib/who_need_help_web/live/request_live/show.ex:820
#, 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:71
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:62
#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:122
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15
#, elixir-autogen, elixir-format
msgid "Email"
@@ -711,7 +701,7 @@ msgstr ""
msgid "English name"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:992
+#: lib/who_need_help_web/live/request_live/show.ex:1005
#, elixir-autogen, elixir-format
msgid "Enter handover code"
msgstr ""
@@ -721,7 +711,7 @@ msgstr ""
msgid "Exact address after matching"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:274
+#: lib/who_need_help_web/live/activity_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Exact coordinates are shown only to participants you approve."
msgstr ""
@@ -731,29 +721,29 @@ msgstr ""
msgid "Exact for active match"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:316
-#: lib/who_need_help_web/live/request_live/show.ex:641
+#: lib/who_need_help_web/live/request_live/new.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:646
#, elixir-autogen, elixir-format
msgid "Exact only for active match"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:169
-#: lib/who_need_help_web/live/request_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:647
#, elixir-autogen, elixir-format
msgid "Exact publicly"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:317
+#: lib/who_need_help_web/live/request_live/new.ex:322
#, elixir-autogen, elixir-format
msgid "Exact publicly — explicit consent"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:710
+#: lib/who_need_help_web/live/request_live/show.ex:715
#, elixir-autogen, elixir-format
msgid "Expires:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:274
+#: lib/who_need_help_web/live/request_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr ""
@@ -763,9 +753,9 @@ msgstr ""
msgid "Find people to join"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:664
+#: lib/who_need_help_web/live/activity_live/show.ex:676
#: lib/who_need_help_web/live/moderation_live.ex:250
-#: lib/who_need_help_web/live/request_live/show.ex:1088
+#: lib/who_need_help_web/live/request_live/show.ex:1106
#, elixir-autogen, elixir-format
msgid "Fraud"
msgstr ""
@@ -785,7 +775,7 @@ msgstr ""
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:975
+#: lib/who_need_help_web/live/request_live/show.ex:988
#, elixir-autogen, elixir-format
msgid "HANDOVER CODE FOR THE HELPER"
msgstr ""
@@ -800,9 +790,9 @@ msgstr ""
msgid "Handover code verified."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:662
+#: lib/who_need_help_web/live/activity_live/show.ex:674
#: lib/who_need_help_web/live/moderation_live.ex:249
-#: lib/who_need_help_web/live/request_live/show.ex:1087
+#: lib/who_need_help_web/live/request_live/show.ex:1105
#, elixir-autogen, elixir-format
msgid "Harassment"
msgstr ""
@@ -822,7 +812,7 @@ msgstr ""
msgid "Helper"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:878
+#: lib/who_need_help_web/live/request_live/show.ex:891
#, elixir-autogen, elixir-format
msgid "Helper: %{name}"
msgstr ""
@@ -852,10 +842,10 @@ 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:259
+#: lib/who_need_help_web/live/activity_live/new.ex:264
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:320
+#: lib/who_need_help_web/live/request_live/show.ex:645
#, elixir-autogen, elixir-format
msgid "Hidden"
msgstr ""
@@ -881,33 +871,34 @@ msgstr ""
msgid "Human decisions retained"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:39
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:85
#, 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:862
+#: lib/who_need_help_web/live/request_live/show.ex:875
#, elixir-autogen, elixir-format
msgid "I can help"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:127
+#: lib/who_need_help_web/live/request_live/new.ex:132
#, 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:121
+#: lib/who_need_help_web/live/request_live/new.ex:126
#, 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:133
+#: lib/who_need_help_web/live/request_live/new.ex:138
#, 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:298
+#: lib/who_need_help_web/live/activity_live/new.ex:303
#, 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 ""
@@ -922,21 +913,21 @@ msgstr ""
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:666
+#: lib/who_need_help_web/live/activity_live/show.ex:678
#: lib/who_need_help_web/live/moderation_live.ex:253
-#: lib/who_need_help_web/live/request_live/show.ex:1091
+#: lib/who_need_help_web/live/request_live/show.ex:1109
#, elixir-autogen, elixir-format
msgid "Impersonation"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1027
+#: lib/who_need_help_web/live/request_live/show.ex:1040
#, 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:180
#: lib/who_need_help_web/live/request_live/index.ex:181
-#: lib/who_need_help_web/live/request_live/show.ex:634
+#: lib/who_need_help_web/live/request_live/show.ex:639
#, elixir-autogen, elixir-format
msgid "In progress"
msgstr ""
@@ -956,49 +947,49 @@ msgstr ""
msgid "Invalid email or password"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:314
+#: lib/who_need_help_web/live/activity_live/show.ex:318
#, elixir-autogen, elixir-format
msgid "Join requests are closed."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:380
+#: lib/who_need_help_web/live/activity_live/show.ex:384
#, elixir-autogen, elixir-format
msgid "Join requests close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:239
+#: lib/who_need_help_web/live/activity_live/new.ex:244
#, elixir-autogen, elixir-format
msgid "Join requests close (UTC)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:282
-#: lib/who_need_help_web/live/request_live/new.ex:340
+#: lib/who_need_help_web/live/activity_live/new.ex:287
+#: lib/who_need_help_web/live/request_live/new.ex:345
#, elixir-autogen, elixir-format
msgid "Latitude"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:534
+#: lib/who_need_help_web/live/activity_live/show.ex:541
#, elixir-autogen, elixir-format
msgid "Leave activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1025
+#: lib/who_need_help_web/live/request_live/show.ex:1038
#, 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:503
+#: lib/who_need_help_web/live/activity_live/show.ex:507
#, elixir-autogen, elixir-format
msgid "Location privacy"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:914
+#: lib/who_need_help_web/live/request_live/show.ex:927
#, 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:312
+#: lib/who_need_help_web/live/request_live/new.ex:317
#, elixir-autogen, elixir-format
msgid "Location visibility"
msgstr ""
@@ -1008,12 +999,12 @@ msgstr ""
msgid "Location-supported people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:717
+#: lib/who_need_help_web/live/request_live/show.ex:722
#, elixir-autogen, elixir-format
msgid "Location:"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:136
#, elixir-autogen, elixir-format
msgid "Log in and stay logged in"
msgstr ""
@@ -1023,23 +1014,18 @@ msgstr ""
msgid "Log in instructions"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:139
#, elixir-autogen, elixir-format
msgid "Log in only this time"
msgstr ""
-#: 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_controller.ex:117
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:289
-#: lib/who_need_help_web/live/request_live/new.ex:347
+#: lib/who_need_help_web/live/activity_live/new.ex:294
+#: lib/who_need_help_web/live/request_live/new.ex:352
#, elixir-autogen, elixir-format
msgid "Longitude"
msgstr ""
@@ -1059,7 +1045,7 @@ msgstr ""
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:591
+#: lib/who_need_help_web/live/activity_live/show.ex:598
#, elixir-autogen, elixir-format
msgid "Mark completed"
msgstr ""
@@ -1071,12 +1057,12 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:637
#, elixir-autogen, elixir-format
msgid "Matched"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:113
+#: lib/who_need_help_web/live/request_live/new.ex:118
#, elixir-autogen, elixir-format
msgid "Medicine is ready at the pharmacy"
msgstr ""
@@ -1091,7 +1077,7 @@ msgstr ""
msgid "Medicine pickup"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:156
+#: lib/who_need_help_web/live/request_live/new.ex:161
#, 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 ""
@@ -1106,7 +1092,7 @@ msgstr ""
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:386
+#: lib/who_need_help_web/live/activity_live/show.ex:390
#, elixir-autogen, elixir-format
msgid "Meeting area"
msgstr ""
@@ -1126,12 +1112,12 @@ msgstr ""
msgid "Merge note"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:483
+#: lib/who_need_help_web/live/activity_live/show.ex:487
#, elixir-autogen, elixir-format
msgid "Message the approved group"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:97
+#: lib/who_need_help_web/live/category_proposal_live.ex:103
#, elixir-autogen, elixir-format
msgid "Mode"
msgstr ""
@@ -1165,7 +1151,7 @@ msgstr ""
msgid "Nearby help"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:116
+#: lib/who_need_help_web/live/request_live/new.ex:121
#, elixir-autogen, elixir-format
msgid "Need roadside help in a safe location"
msgstr ""
@@ -1175,15 +1161,15 @@ msgstr ""
msgid "Network"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:94
#, elixir-autogen, elixir-format
msgid "New password"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:212
+#: lib/who_need_help_web/live/activity_live/show.ex:300
+#: lib/who_need_help_web/live/request_live/new.ex:260
+#: lib/who_need_help_web/live/request_live/show.ex:568
#, elixir-autogen, elixir-format
msgid "No"
msgstr ""
@@ -1193,8 +1179,8 @@ msgstr ""
msgid "No completed help yet."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:451
-#: lib/who_need_help_web/live/request_live/show.ex:771
+#: lib/who_need_help_web/live/activity_live/show.ex:455
+#: lib/who_need_help_web/live/request_live/show.ex:776
#, elixir-autogen, elixir-format
msgid "No messages yet."
msgstr ""
@@ -1214,12 +1200,12 @@ msgstr ""
msgid "No open signals."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:586
+#: lib/who_need_help_web/live/activity_live/show.ex:593
#, elixir-autogen, elixir-format
msgid "No pending join requests."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:134
+#: lib/who_need_help_web/live/category_proposal_live.ex:140
#, elixir-autogen, elixir-format
msgid "No proposals yet."
msgstr ""
@@ -1252,9 +1238,9 @@ msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:168
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:298
+#: lib/who_need_help_web/live/request_live/show.ex:628
+#: lib/who_need_help_web/live/request_live/show.ex:629
#, elixir-autogen, elixir-format
msgid "Now"
msgstr ""
@@ -1279,17 +1265,17 @@ msgstr ""
msgid "Only the conversation linked to this report is displayed."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:437
+#: lib/who_need_help_web/live/activity_live/show.ex:441
#, 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:166
-#: lib/who_need_help_web/live/activity_live/show.ex:338
+#: lib/who_need_help_web/live/activity_live/show.ex:342
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:636
#, elixir-autogen, elixir-format
msgid "Open"
msgstr ""
@@ -1299,7 +1285,7 @@ msgstr ""
msgid "Open abuse signals"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:128
+#: lib/who_need_help_web/live/category_proposal_live.ex:134
#, elixir-autogen, elixir-format
msgid "Open proposals"
msgstr ""
@@ -1309,50 +1295,50 @@ msgstr ""
msgid "Optional external thank-you link"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1011
+#: lib/who_need_help_web/live/request_live/show.ex:1024
#, elixir-autogen, elixir-format
msgid "Optional thank-you link"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:390
+#: lib/who_need_help_web/live/activity_live/show.ex:394
#, elixir-autogen, elixir-format
msgid "Organizer"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:155
+#: lib/who_need_help_web/live/activity_live/show.ex:159
#, elixir-autogen, elixir-format
msgid "Organizer blocked. Their activities and messages are hidden."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:539
+#: lib/who_need_help_web/live/activity_live/show.ex:546
#, elixir-autogen, elixir-format
msgid "Organizer controls"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:399
+#: lib/who_need_help_web/live/activity_live/show.ex:403
#, elixir-autogen, elixir-format
msgid "Organizer social links"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:667
+#: lib/who_need_help_web/live/activity_live/show.ex:679
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:1110
#, elixir-autogen, elixir-format
msgid "Other"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:112
#, elixir-autogen, elixir-format
msgid "Parent (optional)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:459
+#: lib/who_need_help_web/live/activity_live/show.ex:463
#, elixir-autogen, elixir-format
msgid "Participant"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:130
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
@@ -1362,27 +1348,27 @@ msgstr ""
msgid "Password updated successfully."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:280
+#: lib/who_need_help_web/live/request_live/new.ex:285
#, elixir-autogen, elixir-format
msgid "Pickup instructions (optional)"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:686
+#: lib/who_need_help_web/live/request_live/show.ex:691
#, elixir-autogen, elixir-format
msgid "Pickup notes"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:225
+#: lib/who_need_help_web/live/activity_live/new.ex:230
#, elixir-autogen, elixir-format
msgid "Plan and expectations"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:421
+#: lib/who_need_help_web/live/activity_live/show.ex:425
#, elixir-autogen, elixir-format
msgid "Plan details"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:619
+#: lib/who_need_help_web/live/request_live/show.ex:624
#, elixir-autogen, elixir-format
msgid "Please check the entered data."
msgstr ""
@@ -1392,12 +1378,12 @@ msgstr ""
msgid "Please check the submitted fields."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:334
+#: lib/who_need_help_web/live/activity_live/show.ex:338
#, elixir-autogen, elixir-format
msgid "Please check the submitted message."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:307
+#: lib/who_need_help_web/live/request_live/new.ex:312
#, elixir-autogen, elixir-format
msgid "Podil, near the central pharmacy"
msgstr ""
@@ -1413,7 +1399,7 @@ msgstr ""
msgid "Primary navigation"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:755
+#: lib/who_need_help_web/live/request_live/show.ex:760
#, elixir-autogen, elixir-format
msgid "Private match chat"
msgstr ""
@@ -1429,7 +1415,7 @@ msgid "Profile updated."
msgstr ""
#: lib/who_need_help_web/live/moderation_live.ex:251
-#: lib/who_need_help_web/live/request_live/show.ex:1089
+#: lib/who_need_help_web/live/request_live/show.ex:1107
#, elixir-autogen, elixir-format
msgid "Prohibited item"
msgstr ""
@@ -1449,12 +1435,12 @@ msgstr ""
msgid "Proposal rejected."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:86
+#: lib/who_need_help_web/live/category_proposal_live.ex:87
#, elixir-autogen, elixir-format
msgid "Proposals and votes guide moderators. Approval remains a human decision."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:91
+#: lib/who_need_help_web/live/category_proposal_live.ex:97
#, elixir-autogen, elixir-format
msgid "Proposed category"
msgstr ""
@@ -1464,7 +1450,7 @@ 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:256
+#: lib/who_need_help_web/live/activity_live/new.ex:261
#, elixir-autogen, elixir-format
msgid "Public map visibility"
msgstr ""
@@ -1479,28 +1465,28 @@ msgstr ""
msgid "Public reputation"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:117
+#: lib/who_need_help_web/live/activity_live/new.ex:122
#, 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:310
+#: lib/who_need_help_web/live/activity_live/new.ex:315
#, elixir-autogen, elixir-format
msgid "Publish activity"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:123
+#: lib/who_need_help_web/live/category_proposal_live.ex:129
#, elixir-autogen, elixir-format
msgid "Publish proposal"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:364
+#: lib/who_need_help_web/live/request_live/new.ex:369
#, elixir-autogen, elixir-format
msgid "Publish request"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:308
-#: lib/who_need_help_web/live/request_live/new.ex:362
+#: lib/who_need_help_web/live/activity_live/new.ex:313
+#: lib/who_need_help_web/live/request_live/new.ex:367
#, elixir-autogen, elixir-format
msgid "Publishing…"
msgstr ""
@@ -1526,17 +1512,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:823
+#: lib/who_need_help_web/live/request_live/show.ex:836
#, elixir-autogen, elixir-format
msgid "Rating"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:77
#, elixir-autogen, elixir-format
msgid "Read the"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:660
+#: lib/who_need_help_web/live/activity_live/show.ex:672
#, elixir-autogen, elixir-format
msgid "Reason"
msgstr ""
@@ -1561,22 +1547,22 @@ msgstr ""
msgid "Remove"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:468
+#: lib/who_need_help_web/live/activity_live/show.ex:472
#, elixir-autogen, elixir-format
msgid "Report"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:789
+#: lib/who_need_help_web/live/request_live/show.ex:794
#, elixir-autogen, elixir-format
msgid "Report message"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1084
+#: lib/who_need_help_web/live/request_live/show.ex:1102
#, elixir-autogen, elixir-format
msgid "Report reason"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:123
+#: lib/who_need_help_web/live/activity_live/show.ex:126
#: lib/who_need_help_web/live/request_live/show.ex:261
#, elixir-autogen, elixir-format
msgid "Report sent to moderators."
@@ -1597,7 +1583,7 @@ msgstr ""
msgid "Reports"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1053
+#: lib/who_need_help_web/live/request_live/show.ex:1066
#, elixir-autogen, elixir-format
msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages."
msgstr ""
@@ -1607,7 +1593,7 @@ msgstr ""
msgid "Request cancelled."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:301
+#: lib/who_need_help_web/live/request_live/new.ex:306
#, elixir-autogen, elixir-format
msgid "Request expires (UTC)"
msgstr ""
@@ -1622,22 +1608,22 @@ msgstr ""
msgid "Request restored."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:521
+#: lib/who_need_help_web/live/activity_live/show.ex:525
#, elixir-autogen, elixir-format
msgid "Request to join"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:725
+#: lib/who_need_help_web/live/request_live/show.ex:730
#, elixir-autogen, elixir-format
msgid "Requester links:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:706
+#: lib/who_need_help_web/live/request_live/show.ex:711
#, elixir-autogen, elixir-format
msgid "Requester:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:745
#, elixir-autogen, elixir-format
msgid "Requester: %{count} completed"
msgstr ""
@@ -1665,6 +1651,7 @@ msgid "Review note"
msgstr ""
#: lib/who_need_help_web/live/request_live/show.ex:233
+#: lib/who_need_help_web/live/request_live/show.ex:825
#, elixir-autogen, elixir-format
msgid "Review saved. It appears after both participants review."
msgstr ""
@@ -1696,8 +1683,8 @@ msgstr ""
msgid "Russian name (optional)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:635
-#: lib/who_need_help_web/live/request_live/show.ex:1051
+#: lib/who_need_help_web/live/activity_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:1064
#, elixir-autogen, elixir-format
msgid "Safety controls"
msgstr ""
@@ -1720,7 +1707,7 @@ msgstr ""
msgid "Save"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:48
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:108
#, elixir-autogen, elixir-format
msgid "Save Password"
msgstr ""
@@ -1733,21 +1720,21 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:300
+#: lib/who_need_help_web/live/request_live/show.ex:632
+#: lib/who_need_help_web/live/request_live/show.ex:633
#, elixir-autogen, elixir-format
msgid "Scheduled"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:486
-#: lib/who_need_help_web/live/request_live/show.ex:806
+#: lib/who_need_help_web/live/activity_live/show.ex:490
+#: lib/who_need_help_web/live/request_live/show.ex:811
#, elixir-autogen, elixir-format
msgid "Send"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:676
-#: lib/who_need_help_web/live/request_live/show.ex:1101
+#: lib/who_need_help_web/live/activity_live/show.ex:688
+#: lib/who_need_help_web/live/request_live/show.ex:1119
#, elixir-autogen, elixir-format
msgid "Send report"
msgstr ""
@@ -1757,12 +1744,12 @@ 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:1038
+#: lib/who_need_help_web/live/request_live/show.ex:1051
#, elixir-autogen, elixir-format
msgid "Share location"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:282
+#: lib/who_need_help_web/live/request_live/new.ex:287
#, elixir-autogen, elixir-format
msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat."
msgstr ""
@@ -1772,8 +1759,8 @@ msgstr ""
msgid "Short bio"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:219
-#: lib/who_need_help_web/live/request_live/new.ex:266
+#: lib/who_need_help_web/live/activity_live/new.ex:224
+#: lib/who_need_help_web/live/request_live/new.ex:271
#, elixir-autogen, elixir-format
msgid "Short title"
msgstr ""
@@ -1803,8 +1790,8 @@ msgstr ""
msgid "Social activities"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:100
-#: lib/who_need_help_web/live/category_proposal_live.ex:143
+#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:149
#, elixir-autogen, elixir-format
msgid "Social activity"
msgstr ""
@@ -1844,24 +1831,24 @@ msgstr ""
msgid "Social verification session is invalid."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:665
+#: lib/who_need_help_web/live/activity_live/show.ex:677
#: lib/who_need_help_web/live/moderation_live.ex:252
-#: lib/who_need_help_web/live/request_live/show.ex:1090
+#: lib/who_need_help_web/live/request_live/show.ex:1108
#, elixir-autogen, elixir-format
msgid "Spam"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:940
+#: lib/who_need_help_web/live/request_live/show.ex:953
#, elixir-autogen, elixir-format
msgid "Start helping"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:374
+#: lib/who_need_help_web/live/activity_live/show.ex:378
#, elixir-autogen, elixir-format
msgid "Starts"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:234
+#: lib/who_need_help_web/live/activity_live/new.ex:239
#, elixir-autogen, elixir-format
msgid "Starts at (UTC)"
msgstr ""
@@ -1871,7 +1858,7 @@ msgstr ""
msgid "Status"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1046
+#: lib/who_need_help_web/live/request_live/show.ex:1059
#, elixir-autogen, elixir-format
msgid "Stop and delete position"
msgstr ""
@@ -1886,7 +1873,7 @@ msgstr ""
msgid "Structured fields must be a valid JSON object."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:837
+#: lib/who_need_help_web/live/request_live/show.ex:850
#, elixir-autogen, elixir-format
msgid "Submit review"
msgstr ""
@@ -1897,13 +1884,13 @@ msgstr ""
msgid "Suspended"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:604
+#: lib/who_need_help_web/live/request_live/show.ex:609
#, elixir-autogen, elixir-format
msgid "That handover code is not correct."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:140
-#: lib/who_need_help_web/live/request_live/show.ex:278
+#: lib/who_need_help_web/live/activity_live/show.ex:144
+#: lib/who_need_help_web/live/request_live/show.ex:279
#, elixir-autogen, elixir-format
msgid "That message cannot be reported from this view."
msgstr ""
@@ -1913,7 +1900,7 @@ msgstr ""
msgid "That social account is already linked."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:608
+#: lib/who_need_help_web/live/request_live/show.ex:613
#, elixir-autogen, elixir-format
msgid "That step is not available in the current state."
msgstr ""
@@ -1928,7 +1915,7 @@ msgstr ""
msgid "That verification provider is not supported."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:312
+#: lib/who_need_help_web/live/activity_live/show.ex:316
#, elixir-autogen, elixir-format
msgid "The approved group has reached its capacity."
msgstr ""
@@ -1948,7 +1935,7 @@ msgstr ""
msgid "The link is invalid or it has expired."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:526
+#: lib/who_need_help_web/live/activity_live/show.ex:530
#, elixir-autogen, elixir-format
msgid "The organizer has not approved your request yet."
msgstr ""
@@ -1963,48 +1950,48 @@ msgstr ""
msgid "The platform never handles, requires, splits, or guarantees a payment."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:508
+#: lib/who_need_help_web/live/activity_live/show.ex:512
#, 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:648
+#: lib/who_need_help_web/live/activity_live/show.ex:660
#, 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:1076
+#: lib/who_need_help_web/live/request_live/show.ex:1094
#, 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:315
+#: lib/who_need_help_web/live/activity_live/show.ex:319
#, elixir-autogen, elixir-format
msgid "This action is unavailable."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:322
+#: lib/who_need_help_web/live/activity_live/show.ex:326
#, elixir-autogen, elixir-format
msgid "This activity is no longer open."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:320
+#: lib/who_need_help_web/live/activity_live/show.ex:324
#, elixir-autogen, elixir-format
msgid "This activity state has already changed."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:613
+#: lib/who_need_help_web/live/request_live/show.ex:618
#, elixir-autogen, elixir-format
msgid "This interaction is blocked."
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:210
-#: lib/who_need_help_web/live/request_live/show.ex:665
+#: lib/who_need_help_web/live/request_live/show.ex:670
#, 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:164
+#: lib/who_need_help_web/live/request_live/new.ex:169
#, 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 ""
@@ -2014,7 +2001,7 @@ msgstr ""
msgid "This proposal has already been reviewed."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:56
+#: lib/who_need_help_web/live/category_proposal_live.ex:57
#, elixir-autogen, elixir-format
msgid "This proposal is already closed."
msgstr ""
@@ -2024,13 +2011,13 @@ msgstr ""
msgid "This report has no linked conversation."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1016
+#: lib/who_need_help_web/live/request_live/show.ex:1029
#, elixir-autogen, elixir-format
msgid "This request already has a helper."
msgstr ""
#: lib/who_need_help_web/live/request_live/show.ex:74
-#: lib/who_need_help_web/live/request_live/show.ex:615
+#: lib/who_need_help_web/live/request_live/show.ex:620
#, elixir-autogen, elixir-format
msgid "This request is no longer available."
msgstr ""
@@ -2048,28 +2035,29 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:299
+#: lib/who_need_help_web/live/request_live/show.ex:630
+#: lib/who_need_help_web/live/request_live/show.ex:631
#, elixir-autogen, elixir-format
msgid "Today"
msgstr ""
-#: 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/activity_live/new.ex:108
+#: lib/who_need_help_web/live/activity_live/show.ex:336
+#: lib/who_need_help_web/live/category_proposal_live.ex:55
#: lib/who_need_help_web/live/profile_live.ex:59
-#: lib/who_need_help_web/live/request_live/show.ex:618
+#: lib/who_need_help_web/live/request_live/show.ex:623
#, elixir-autogen, elixir-format
msgid "Too many actions in the configured time window."
msgstr ""
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:180
#: 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:142
+#: lib/who_need_help_web/live/request_live/new.ex:147
#, elixir-autogen, elixir-format
msgid "Too many requests in the configured time window."
msgstr ""
@@ -2079,7 +2067,7 @@ msgstr ""
msgid "Too many sign-in emails in the configured time window."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:107
+#: lib/who_need_help_web/live/category_proposal_live.ex:113
#, elixir-autogen, elixir-format
msgid "Top level"
msgstr ""
@@ -2089,7 +2077,7 @@ msgstr ""
msgid "Total"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:246
+#: lib/who_need_help_web/live/activity_live/new.ex:251
#, elixir-autogen, elixir-format
msgid "Total group capacity"
msgstr ""
@@ -2109,7 +2097,7 @@ msgstr ""
msgid "Unblock"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1069
+#: lib/who_need_help_web/live/request_live/show.ex:1082
#, elixir-autogen, elixir-format
msgid "Unblock this user"
msgstr ""
@@ -2125,18 +2113,18 @@ msgid "Update email instructions"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:240
-#: lib/who_need_help_web/live/request_live/new.ex:291
+#: lib/who_need_help_web/live/request_live/new.ex:296
#, elixir-autogen, elixir-format
msgid "Urgency"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:99
-#: lib/who_need_help_web/live/category_proposal_live.ex:142
+#: lib/who_need_help_web/live/category_proposal_live.ex:105
+#: lib/who_need_help_web/live/category_proposal_live.ex:148
#, elixir-autogen, elixir-format
msgid "Urgent help"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:123
+#: lib/who_need_help_web/live/activity_live/new.ex:128
#, 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 ""
@@ -2151,8 +2139,8 @@ msgstr ""
msgid "Use light theme"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:271
-#: lib/who_need_help_web/live/request_live/new.ex:329
+#: lib/who_need_help_web/live/activity_live/new.ex:276
+#: lib/who_need_help_web/live/request_live/new.ex:334
#, elixir-autogen, elixir-format
msgid "Use my current foreground location"
msgstr ""
@@ -2174,7 +2162,7 @@ msgstr ""
msgid "User"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:307
+#: lib/who_need_help_web/live/request_live/show.ex:308
#, elixir-autogen, elixir-format
msgid "User blocked. Their requests and new messages are hidden."
msgstr ""
@@ -2195,7 +2183,7 @@ msgid "User status updated."
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:87
-#: lib/who_need_help_web/live/request_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:322
#, elixir-autogen, elixir-format
msgid "User unblocked."
msgstr ""
@@ -2210,7 +2198,7 @@ msgstr ""
msgid "Verify GitHub"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:996
+#: lib/who_need_help_web/live/request_live/show.ex:1009
#, elixir-autogen, elixir-format
msgid "Verify handover"
msgstr ""
@@ -2230,11 +2218,12 @@ msgstr ""
msgid "Visible reviews"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:866
+#: lib/who_need_help_web/live/request_live/show.ex:879
#, elixir-autogen, elixir-format
msgid "Waiting for a nearby volunteer."
msgstr ""
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:137
#: 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
@@ -2246,18 +2235,18 @@ msgstr ""
msgid "What Codex contributed"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:673
-#: lib/who_need_help_web/live/request_live/show.ex:1098
+#: lib/who_need_help_web/live/activity_live/show.ex:685
+#: lib/who_need_help_web/live/request_live/show.ex:1116
#, elixir-autogen, elixir-format
msgid "What happened?"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:83
+#: lib/who_need_help_web/live/category_proposal_live.ex:84
#, elixir-autogen, elixir-format
msgid "What help category is missing?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:272
+#: lib/who_need_help_web/live/request_live/new.ex:277
#, elixir-autogen, elixir-format
msgid "What help do you need?"
msgstr ""
@@ -2277,7 +2266,7 @@ msgstr ""
msgid "Who Need Help home"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:90
#, 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 ""
@@ -2292,40 +2281,40 @@ msgstr ""
msgid "Who needs help?"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:121
+#: lib/who_need_help_web/live/category_proposal_live.ex:127
#, elixir-autogen, elixir-format
msgid "Why is this useful?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:957
+#: lib/who_need_help_web/live/request_live/show.ex:970
#, elixir-autogen, elixir-format
msgid "Withdraw from this request"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:802
+#: lib/who_need_help_web/live/request_live/show.ex:807
#, elixir-autogen, elixir-format
msgid "Write a safe coordination message…"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:209
+#: lib/who_need_help_web/live/activity_live/show.ex:298
+#: lib/who_need_help_web/live/request_live/new.ex:257
+#: lib/who_need_help_web/live/request_live/show.ex:566
#, elixir-autogen, elixir-format
msgid "Yes"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:309
+#: lib/who_need_help_web/live/activity_live/show.ex:313
#, 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:58
+#: lib/who_need_help_web/live/category_proposal_live.ex:59
#, elixir-autogen, elixir-format
msgid "You already voted for this proposal."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:506
+#: lib/who_need_help_web/live/activity_live/show.ex:510
#, elixir-autogen, elixir-format
msgid "You are approved, so the map can show the exact meeting coordinates."
msgstr ""
@@ -2335,8 +2324,8 @@ msgstr ""
msgid "You are matched. Use chat to coordinate safely."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:317
-#: lib/who_need_help_web/live/request_live/show.ex:605
+#: lib/who_need_help_web/live/activity_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:610
#, elixir-autogen, elixir-format
msgid "You are not allowed to do that."
msgstr ""
@@ -2346,7 +2335,7 @@ msgstr ""
msgid "You are running the local mail adapter."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:332
+#: lib/who_need_help_web/live/request_live/new.ex:337
#, elixir-autogen, elixir-format
msgid "You can also enter coordinates manually for local testing."
msgstr ""
@@ -2356,12 +2345,12 @@ msgstr ""
msgid "You can be the first person to ask the community."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:602
+#: lib/who_need_help_web/live/request_live/show.ex:607
#, elixir-autogen, elixir-format
msgid "You cannot accept your own request."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:316
+#: lib/who_need_help_web/live/activity_live/show.ex:320
#, elixir-autogen, elixir-format
msgid "You cannot report your own content."
msgstr ""
@@ -2413,7 +2402,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:47
+#: lib/who_need_help_web/live/request_live/new.ex:52
#, elixir-autogen, elixir-format
msgid "Your request is now visible to nearby helpers."
msgstr ""
@@ -2423,7 +2412,7 @@ msgstr ""
msgid "Your requests"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:817
+#: lib/who_need_help_web/live/request_live/show.ex:822
#, elixir-autogen, elixir-format
msgid "Your review is revealed only after both participants submit."
msgstr ""
@@ -2443,13 +2432,13 @@ msgstr ""
msgid "activity message"
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:79
#, elixir-autogen, elixir-format
msgid "before confirming."
msgstr ""
#: 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/category_proposal_live.ex:153
#: 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
@@ -2458,7 +2447,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:896
#, elixir-autogen, elixir-format
msgid "completed"
msgstr ""
@@ -2473,59 +2462,54 @@ msgstr ""
msgid "for an account now."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:910
+#: lib/who_need_help_web/live/request_live/show.ex:923
#, elixir-autogen, elixir-format
msgid "helper movement supported"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:911
+#: lib/who_need_help_web/live/request_live/show.ex:924
#, elixir-autogen, elixir-format
msgid "no movement evidence"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:902
+#: lib/who_need_help_web/live/request_live/show.ex:915
#, elixir-autogen, elixir-format
msgid "no proximity evidence"
msgstr ""
-#: 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:605
+#: lib/who_need_help_web/live/activity_live/show.ex:612
#, elixir-autogen, elixir-format
msgid "organizer"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:756
+#: lib/who_need_help_web/live/request_live/show.ex:761
#, elixir-autogen, elixir-format
msgid "participants only"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:328
-#: lib/who_need_help_web/live/request_live/show.ex:887
+#: lib/who_need_help_web/live/request_live/show.ex:900
#, elixir-autogen, elixir-format
msgid "people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:901
+#: lib/who_need_help_web/live/request_live/show.ex:914
#, elixir-autogen, elixir-format
msgid "proximity supported"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:204
-#: lib/who_need_help_web/live/request_live/show.ex:891
+#: lib/who_need_help_web/live/request_live/show.ex:904
#, elixir-autogen, elixir-format
msgid "rating"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:748
+#: lib/who_need_help_web/live/request_live/show.ex:753
#, elixir-autogen, elixir-format
msgid "rating %{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:78
#, elixir-autogen, elixir-format
msgid "safety rules"
msgstr ""
@@ -2545,12 +2529,12 @@ msgstr ""
msgid "unique people"
msgstr ""
-#: 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/activity_live/show.ex:418
+#: lib/who_need_help_web/live/activity_live/show.ex:572
+#: lib/who_need_help_web/live/activity_live/show.ex:631
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:945
#, elixir-autogen, elixir-format
msgid "unverified"
msgstr ""
@@ -2560,13 +2544,13 @@ msgstr ""
msgid "urgent"
msgstr ""
-#: 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/activity_live/show.ex:417
+#: lib/who_need_help_web/live/activity_live/show.ex:571
+#: lib/who_need_help_web/live/activity_live/show.ex:630
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:739
+#: lib/who_need_help_web/live/request_live/show.ex:944
#, elixir-autogen, elixir-format
msgid "verified"
msgstr ""
@@ -2576,18 +2560,18 @@ msgstr ""
msgid "verified handovers"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:660
+#: lib/who_need_help_web/live/request_live/show.ex:665
#, elixir-autogen, elixir-format
msgid "← All requests"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:113
-#: lib/who_need_help_web/live/activity_live/show.ex:348
+#: lib/who_need_help_web/live/activity_live/new.ex:118
+#: lib/who_need_help_web/live/activity_live/show.ex:352
#, elixir-autogen, elixir-format
msgid "← Back to activities"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:152
+#: lib/who_need_help_web/live/request_live/new.ex:157
#, elixir-autogen, elixir-format
msgid "← Back to requests"
msgstr ""
@@ -2648,15 +2632,15 @@ msgstr ""
msgid "Unusual action rate"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:448
-#: lib/who_need_help_web/live/request_live/show.ex:768
+#: lib/who_need_help_web/live/activity_live/show.ex:452
+#: lib/who_need_help_web/live/request_live/show.ex:773
#, elixir-autogen, elixir-format
msgid "Load earlier messages"
msgstr ""
#: 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/category_proposal_live.ex:168
#: lib/who_need_help_web/live/leaderboard_live.ex:87
#: lib/who_need_help_web/live/moderation_live.ex:384
#: lib/who_need_help_web/live/moderation_live.ex:419
@@ -2671,9 +2655,9 @@ msgid "Load more"
msgstr ""
#: 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/activity_live/show.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:314
-#: lib/who_need_help_web/live/request_live/show.ex:849
+#: lib/who_need_help_web/live/request_live/show.ex:862
#, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable."
msgstr ""
@@ -2694,19 +2678,19 @@ msgstr ""
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
+#: lib/who_need_help_web/live/activity_live/show.ex:340
+#: lib/who_need_help_web/live/category_proposal_live.ex:61
+#: lib/who_need_help_web/live/request_live/show.ex:626
#, 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
+#: lib/who_need_help_web/live/activity_live/new.ex:110
#, 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
+#: lib/who_need_help_web/live/request_live/new.ex:149
#, elixir-autogen, elixir-format
msgid "Could not publish the request. Please try again."
msgstr ""
@@ -2721,32 +2705,32 @@ msgstr ""
msgid "JavaScript is required to confirm this protected email link."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:594
+#: lib/who_need_help_web/live/request_live/show.ex:599
#, elixir-autogen, elixir-format
msgid "Last update: %{time}"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:617
+#: lib/who_need_help_web/live/request_live/show.ex:622
#, elixir-autogen, elixir-format
msgid "Live location sharing is not active."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:592
+#: lib/who_need_help_web/live/request_live/show.ex:597
#, elixir-autogen, elixir-format
msgid "Shared location"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:329
+#: lib/who_need_help_web/live/activity_live/show.ex:333
#, elixir-autogen, elixir-format
msgid "The organizer cannot leave the activity."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:326
+#: lib/who_need_help_web/live/activity_live/show.ex:330
#, elixir-autogen, elixir-format
msgid "The organizer is already attending."
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_controller.ex:60
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:58
#, elixir-autogen, elixir-format
msgid "The registration form is invalid."
msgstr ""
@@ -2766,22 +2750,22 @@ msgstr ""
msgid "The sign-in form is invalid."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:323
+#: lib/who_need_help_web/live/activity_live/show.ex:327
#, elixir-autogen, elixir-format
msgid "This activity is no longer available."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:616
+#: lib/who_need_help_web/live/request_live/show.ex:621
#, elixir-autogen, elixir-format
msgid "This match is no longer active."
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 "This proposal is no longer available."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:614
+#: lib/who_need_help_web/live/request_live/show.ex:619
#, elixir-autogen, elixir-format
msgid "This request has expired."
msgstr ""
@@ -2796,12 +2780,12 @@ msgstr ""
msgid "Your secure email link is ready. Continue to sign in."
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:128
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:130
#, 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
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:107
#, elixir-autogen, elixir-format
msgid "If this address can be registered or signed in, login instructions will arrive shortly."
msgstr ""
@@ -2815,3 +2799,185 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Too many email-change requests in the configured time window."
msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:32
+#, elixir-autogen, elixir-format
+msgid "A Google account is connected. You can use it to sign in."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:207
+#, elixir-autogen, elixir-format
+msgid "A different Google account is already connected."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:17
+#, elixir-autogen, elixir-format
+msgid "A password is not required. We will email a one-time confirmation link. You can add a password later in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:170
+#, elixir-autogen, elixir-format
+msgid "An account with this email already exists. Sign in by email or password, then connect Google in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:37
+#, elixir-autogen, elixir-format
+msgid "Confirm that you are 18 or older and accept the safety rules first."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:49
+#, elixir-autogen, elixir-format
+msgid "Connect Google account"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#, elixir-autogen, elixir-format
+msgid "Connect Google only after signing in here. We never merge accounts automatically by matching email addresses."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "Create account and email me a link"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:99
+#, elixir-autogen, elixir-format
+msgid "Email me a sign-in link"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:215
+#, elixir-autogen, elixir-format
+msgid "Google account connection session is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:97
+#, elixir-autogen, elixir-format
+msgid "Google did not provide a verified email address."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:29
+#, elixir-autogen, elixir-format
+msgid "Google sign-in"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:197
+#, elixir-autogen, elixir-format
+msgid "Google sign-in connected."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:123
+#, elixir-autogen, elixir-format
+msgid "Google sign-in could not be started."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:102
+#, elixir-autogen, elixir-format
+msgid "Google sign-in failed. Please try again."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:128
+#, elixir-autogen, elixir-format
+msgid "Google sign-in is not configured yet."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:87
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session expired. Please start again."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:91
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:47
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:53
+#, elixir-autogen, elixir-format
+msgid "Google sign-in will be available after the operator configures it."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:95
+#, elixir-autogen, elixir-format
+msgid "Sending link..."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:74
+#, elixir-autogen, elixir-format
+msgid "Sign in with Google"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43
+#, elixir-autogen, elixir-format
+msgid "Sign up with Google"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:202
+#, elixir-autogen, elixir-format
+msgid "That Google account belongs to another local account."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:45
+#, elixir-autogen, elixir-format
+msgid "The Google registration form is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:260
+#, elixir-autogen, elixir-format
+msgid "This Google account cannot be used right now."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:144
+#, elixir-autogen, elixir-format
+msgid "This Google account is not connected yet. Create an account or sign in by email and connect Google in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:109
+#, elixir-autogen, elixir-format
+msgid "This works only if you previously added a password in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#, elixir-autogen, elixir-format
+msgid "Use a password instead"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "We will send a one-time sign-in link. No password is required."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:163
+#, elixir-autogen, elixir-format
+msgid "Your account was created with Google."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:51
+#, elixir-autogen, elixir-format
+msgid "or sign up with email"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82
+#, elixir-autogen, elixir-format
+msgid "or use email"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:70
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:68
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google sign-in from this account?"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:60
+#, elixir-autogen, elixir-format
+msgid "Google sign-in disconnected."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:65
+#, elixir-autogen, elixir-format
+msgid "No Google account is connected."
+msgstr ""
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
index bc2379b..6aac897 100644
--- a/priv/gettext/en/LC_MESSAGES/default.po
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -17,7 +17,7 @@ msgstr ""
msgid "Account settings"
msgstr ""
-#: lib/who_need_help_web/components/core_components.ex:393
+#: lib/who_need_help_web/components/core_components.ex:446
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
@@ -25,7 +25,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:23
#, elixir-autogen, elixir-format
msgid "Ask for help"
msgstr ""
@@ -108,7 +108,7 @@ msgstr ""
msgid "close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:363
+#: lib/who_need_help_web/live/activity_live/show.ex:367
#, elixir-autogen, elixir-format
msgid "%{approved}/%{capacity} approved"
msgstr ""
@@ -118,37 +118,37 @@ msgstr ""
msgid "%{count} community votes"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:745
+#: lib/who_need_help_web/live/request_live/show.ex:750
#, elixir-autogen, elixir-format
msgid "%{count} unique people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:829
+#: lib/who_need_help_web/live/request_live/show.ex:842
#, elixir-autogen, elixir-format
msgid "1 — unsafe"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:828
+#: lib/who_need_help_web/live/request_live/show.ex:841
#, elixir-autogen, elixir-format
msgid "2 — poor"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:827
+#: lib/who_need_help_web/live/request_live/show.ex:840
#, elixir-autogen, elixir-format
msgid "3 — okay"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:826
+#: lib/who_need_help_web/live/request_live/show.ex:839
#, elixir-autogen, elixir-format
msgid "4 — good"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:825
+#: lib/who_need_help_web/live/request_live/show.ex:838
#, elixir-autogen, elixir-format
msgid "5 — excellent"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:118
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:120
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
@@ -158,12 +158,12 @@ msgstr ""
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:859
+#: lib/who_need_help_web/live/request_live/show.ex:872
#, 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:633
+#: lib/who_need_help_web/live/request_live/show.ex:638
#, elixir-autogen, elixir-format
msgid "Accepted"
msgstr ""
@@ -184,7 +184,7 @@ msgstr ""
msgid "Accounts"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:855
+#: lib/who_need_help_web/live/request_live/show.ex:868
#, elixir-autogen, elixir-format
msgid "Action center"
msgstr ""
@@ -206,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:163
+#: lib/who_need_help_web/live/activity_live/new.ex:168
#, elixir-autogen, elixir-format
msgid "Activity details"
msgstr ""
@@ -217,12 +217,12 @@ msgid "Activity hidden."
msgstr ""
#: lib/who_need_help_web/live/activity_live/show.ex:18
-#: lib/who_need_help_web/live/activity_live/show.ex:177
+#: lib/who_need_help_web/live/activity_live/show.ex:181
#, elixir-autogen, elixir-format
msgid "Activity is unavailable."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:36
+#: lib/who_need_help_web/live/activity_live/new.ex:41
#, elixir-autogen, elixir-format
msgid "Activity published. You approve every participant."
msgstr ""
@@ -233,7 +233,7 @@ msgid "Activity restored."
msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:210
-#: lib/who_need_help_web/live/activity_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Activity type"
msgstr ""
@@ -264,7 +264,7 @@ msgstr ""
msgid "Already registered?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:603
+#: lib/who_need_help_web/live/request_live/show.ex:608
#, elixir-autogen, elixir-format
msgid "Another helper already accepted this request."
msgstr ""
@@ -274,60 +274,60 @@ msgstr ""
msgid "Any urgency"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:524
+#: lib/who_need_help_web/live/activity_live/show.ex:528
#, elixir-autogen, elixir-format
msgid "Approval pending"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:574
+#: lib/who_need_help_web/live/activity_live/show.ex:581
#, elixir-autogen, elixir-format
msgid "Approve"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:435
+#: lib/who_need_help_web/live/activity_live/show.ex:439
#, elixir-autogen, elixir-format
msgid "Approved group chat"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:600
+#: lib/who_need_help_web/live/activity_live/show.ex:607
#, 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:258
-#: lib/who_need_help_web/live/request_live/new.ex:306
+#: lib/who_need_help_web/live/activity_live/new.ex:263
+#: lib/who_need_help_web/live/request_live/new.ex:311
#, elixir-autogen, elixir-format
msgid "Approximate area"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:250
+#: lib/who_need_help_web/live/activity_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Approximate public meeting area"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:167
-#: lib/who_need_help_web/live/request_live/show.ex:639
+#: lib/who_need_help_web/live/request_live/show.ex:644
#, elixir-autogen, elixir-format
msgid "Approximate publicly"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:314
+#: lib/who_need_help_web/live/request_live/new.ex:319
#, elixir-autogen, elixir-format
msgid "Approximate publicly (recommended)"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:702
+#: lib/who_need_help_web/live/request_live/show.ex:707
#, elixir-autogen, elixir-format
msgid "Area:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:154
+#: lib/who_need_help_web/live/request_live/new.ex:159
#, elixir-autogen, elixir-format
msgid "Ask for urgent help"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:92
+#: lib/who_need_help_web/live/category_proposal_live.ex:98
#, elixir-autogen, elixir-format
msgid "Bicycle puncture"
msgstr ""
@@ -342,12 +342,12 @@ msgstr ""
msgid "Block and report"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:641
+#: lib/who_need_help_web/live/activity_live/show.ex:648
#, elixir-autogen, elixir-format
msgid "Block organizer"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1062
+#: lib/who_need_help_web/live/request_live/show.ex:1075
#, elixir-autogen, elixir-format
msgid "Block this user"
msgstr ""
@@ -362,7 +362,7 @@ msgstr ""
msgid "Both confirm and the helper enters a one-time code."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:118
+#: lib/who_need_help_web/live/request_live/new.ex:123
#, elixir-autogen, elixir-format
msgid "Briefly describe the help you need"
msgstr ""
@@ -382,33 +382,33 @@ msgstr ""
msgid "COMMUNITY TRUST"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:80
+#: lib/who_need_help_web/live/category_proposal_live.ex:81
#, elixir-autogen, elixir-format
msgid "COMMUNITY-DRIVEN TAXONOMY"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:594
+#: lib/who_need_help_web/live/activity_live/show.ex:601
#, elixir-autogen, elixir-format
msgid "Cancel activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:873
-#: lib/who_need_help_web/live/request_live/show.ex:967
+#: lib/who_need_help_web/live/request_live/show.ex:886
+#: lib/who_need_help_web/live/request_live/show.ex:980
#, elixir-autogen, elixir-format
msgid "Cancel request"
msgstr ""
#: 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/activity_live/show.ex:344
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:641
#, elixir-autogen, elixir-format
msgid "Cancelled"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:225
-#: lib/who_need_help_web/live/request_live/new.ex:180
+#: lib/who_need_help_web/live/request_live/new.ex:185
#, elixir-autogen, elixir-format
msgid "Category"
msgstr ""
@@ -418,13 +418,13 @@ msgstr ""
msgid "Category created and proposal approved."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:211
-#: lib/who_need_help_web/live/request_live/show.ex:691
+#: lib/who_need_help_web/live/request_live/new.ex:216
+#: lib/who_need_help_web/live/request_live/show.ex:696
#, 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/category_proposal_live.ex:67
#: lib/who_need_help_web/live/moderation_live.ex:466
#, elixir-autogen, elixir-format
msgid "Category proposals"
@@ -441,7 +441,7 @@ msgid "Change role"
msgstr ""
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:21
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:47
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:107
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr ""
@@ -451,31 +451,31 @@ msgstr ""
msgid "Choose a category, urgency, and safe location visibility."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:140
+#: lib/who_need_help_web/live/activity_live/new.ex:145
#, elixir-autogen, elixir-format
msgid "Choose activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:181
+#: lib/who_need_help_web/live/request_live/new.ex:186
#, elixir-autogen, elixir-format
msgid "Choose category"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:180
+#: lib/who_need_help_web/live/activity_live/new.ex:207
+#: lib/who_need_help_web/live/request_live/new.ex:228
+#: lib/who_need_help_web/live/request_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Choose…"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:251
+#: lib/who_need_help_web/live/activity_live/new.ex:256
#, elixir-autogen, elixir-format
msgid "City centre, near the main entrance"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:654
-#: lib/who_need_help_web/live/request_live/show.ex:1078
+#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/request_live/show.ex:1096
#, elixir-autogen, elixir-format
msgid "Clear"
msgstr ""
@@ -485,7 +485,7 @@ msgstr ""
msgid "Close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:220
+#: lib/who_need_help_web/live/activity_live/new.ex:225
#, elixir-autogen, elixir-format
msgid "Coffee and conversation"
msgstr ""
@@ -495,7 +495,7 @@ msgstr ""
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:835
+#: lib/who_need_help_web/live/request_live/show.ex:848
#, elixir-autogen, elixir-format
msgid "Comment (optional)"
msgstr ""
@@ -506,19 +506,19 @@ msgid "Community helpers"
msgstr ""
#: 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/activity_live/show.ex:396
+#: lib/who_need_help_web/live/activity_live/show.ex:553
+#: lib/who_need_help_web/live/activity_live/show.ex:610
#: 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:167
-#: lib/who_need_help_web/live/activity_live/show.ex:339
+#: lib/who_need_help_web/live/activity_live/show.ex:343
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:640
#, elixir-autogen, elixir-format
msgid "Completed"
msgstr ""
@@ -533,24 +533,24 @@ msgstr ""
msgid "Confirm and stay logged in"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:947
+#: lib/who_need_help_web/live/request_live/show.ex:960
#, elixir-autogen, elixir-format
msgid "Confirm my part is complete"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:42
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:102
#, elixir-autogen, elixir-format
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:611
+#: lib/who_need_help_web/live/category_proposal_live.ex:52
+#: lib/who_need_help_web/live/request_live/show.ex:616
#, 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:100
-#: lib/who_need_help_web/live/request_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:105
+#: lib/who_need_help_web/live/request_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Confirm your email and ensure your account is active before publishing."
msgstr ""
@@ -573,17 +573,12 @@ msgid "Create a clear plan in a public place."
msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:188
-#: lib/who_need_help_web/live/activity_live/new.ex:11
+#: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format
msgid "Create activity"
msgstr ""
-#: 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:115
+#: lib/who_need_help_web/live/activity_live/new.ex:120
#, elixir-autogen, elixir-format
msgid "Create an activity"
msgstr ""
@@ -598,18 +593,13 @@ msgstr ""
msgid "Create urgent request"
msgstr ""
-#: 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:663
+#: lib/who_need_help_web/live/activity_live/show.ex:675
#, elixir-autogen, elixir-format
msgid "Dangerous plan"
msgstr ""
#: lib/who_need_help_web/live/moderation_live.ex:248
-#: lib/who_need_help_web/live/request_live/show.ex:1086
+#: lib/who_need_help_web/live/request_live/show.ex:1104
#, elixir-autogen, elixir-format
msgid "Dangerous request"
msgstr ""
@@ -629,7 +619,7 @@ msgstr ""
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:581
+#: lib/who_need_help_web/live/activity_live/show.ex:588
#, elixir-autogen, elixir-format
msgid "Decline"
msgstr ""
@@ -639,7 +629,7 @@ msgstr ""
msgid "Default location visibility"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:227
+#: lib/who_need_help_web/live/activity_live/new.ex:232
#, elixir-autogen, elixir-format
msgid "Describe the public plan, approximate duration, and who might enjoy it."
msgstr ""
@@ -651,7 +641,7 @@ msgstr ""
msgid "Dismissed"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:71
#: lib/who_need_help_web/live/profile_live.ex:149
#, elixir-autogen, elixir-format
msgid "Display name"
@@ -667,14 +657,14 @@ msgstr ""
msgid "Don't have an account?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:815
+#: lib/who_need_help_web/live/request_live/show.ex:820
#, 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:71
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:62
#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:122
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15
#, elixir-autogen, elixir-format
msgid "Email"
@@ -711,7 +701,7 @@ msgstr ""
msgid "English name"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:992
+#: lib/who_need_help_web/live/request_live/show.ex:1005
#, elixir-autogen, elixir-format
msgid "Enter handover code"
msgstr ""
@@ -721,7 +711,7 @@ msgstr ""
msgid "Exact address after matching"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:274
+#: lib/who_need_help_web/live/activity_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Exact coordinates are shown only to participants you approve."
msgstr ""
@@ -731,29 +721,29 @@ msgstr ""
msgid "Exact for active match"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:316
-#: lib/who_need_help_web/live/request_live/show.ex:641
+#: lib/who_need_help_web/live/request_live/new.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:646
#, elixir-autogen, elixir-format
msgid "Exact only for active match"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:169
-#: lib/who_need_help_web/live/request_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:647
#, elixir-autogen, elixir-format
msgid "Exact publicly"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:317
+#: lib/who_need_help_web/live/request_live/new.ex:322
#, elixir-autogen, elixir-format
msgid "Exact publicly — explicit consent"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:710
+#: lib/who_need_help_web/live/request_live/show.ex:715
#, elixir-autogen, elixir-format
msgid "Expires:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:274
+#: lib/who_need_help_web/live/request_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr ""
@@ -763,9 +753,9 @@ msgstr ""
msgid "Find people to join"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:664
+#: lib/who_need_help_web/live/activity_live/show.ex:676
#: lib/who_need_help_web/live/moderation_live.ex:250
-#: lib/who_need_help_web/live/request_live/show.ex:1088
+#: lib/who_need_help_web/live/request_live/show.ex:1106
#, elixir-autogen, elixir-format
msgid "Fraud"
msgstr ""
@@ -785,7 +775,7 @@ msgstr ""
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:975
+#: lib/who_need_help_web/live/request_live/show.ex:988
#, elixir-autogen, elixir-format
msgid "HANDOVER CODE FOR THE HELPER"
msgstr ""
@@ -800,9 +790,9 @@ msgstr ""
msgid "Handover code verified."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:662
+#: lib/who_need_help_web/live/activity_live/show.ex:674
#: lib/who_need_help_web/live/moderation_live.ex:249
-#: lib/who_need_help_web/live/request_live/show.ex:1087
+#: lib/who_need_help_web/live/request_live/show.ex:1105
#, elixir-autogen, elixir-format
msgid "Harassment"
msgstr ""
@@ -822,7 +812,7 @@ msgstr ""
msgid "Helper"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:878
+#: lib/who_need_help_web/live/request_live/show.ex:891
#, elixir-autogen, elixir-format
msgid "Helper: %{name}"
msgstr ""
@@ -852,10 +842,10 @@ 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:259
+#: lib/who_need_help_web/live/activity_live/new.ex:264
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:320
+#: lib/who_need_help_web/live/request_live/show.ex:645
#, elixir-autogen, elixir-format
msgid "Hidden"
msgstr ""
@@ -881,33 +871,34 @@ msgstr ""
msgid "Human decisions retained"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:39
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:85
#, 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:862
+#: lib/who_need_help_web/live/request_live/show.ex:875
#, elixir-autogen, elixir-format
msgid "I can help"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:127
+#: lib/who_need_help_web/live/request_live/new.ex:132
#, 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:121
+#: lib/who_need_help_web/live/request_live/new.ex:126
#, 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:133
+#: lib/who_need_help_web/live/request_live/new.ex:138
#, 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:298
+#: lib/who_need_help_web/live/activity_live/new.ex:303
#, 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 ""
@@ -922,21 +913,21 @@ msgstr ""
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:666
+#: lib/who_need_help_web/live/activity_live/show.ex:678
#: lib/who_need_help_web/live/moderation_live.ex:253
-#: lib/who_need_help_web/live/request_live/show.ex:1091
+#: lib/who_need_help_web/live/request_live/show.ex:1109
#, elixir-autogen, elixir-format
msgid "Impersonation"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1027
+#: lib/who_need_help_web/live/request_live/show.ex:1040
#, 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:180
#: lib/who_need_help_web/live/request_live/index.ex:181
-#: lib/who_need_help_web/live/request_live/show.ex:634
+#: lib/who_need_help_web/live/request_live/show.ex:639
#, elixir-autogen, elixir-format
msgid "In progress"
msgstr ""
@@ -956,49 +947,49 @@ msgstr ""
msgid "Invalid email or password"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:314
+#: lib/who_need_help_web/live/activity_live/show.ex:318
#, elixir-autogen, elixir-format
msgid "Join requests are closed."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:380
+#: lib/who_need_help_web/live/activity_live/show.ex:384
#, elixir-autogen, elixir-format
msgid "Join requests close"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:239
+#: lib/who_need_help_web/live/activity_live/new.ex:244
#, elixir-autogen, elixir-format
msgid "Join requests close (UTC)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:282
-#: lib/who_need_help_web/live/request_live/new.ex:340
+#: lib/who_need_help_web/live/activity_live/new.ex:287
+#: lib/who_need_help_web/live/request_live/new.ex:345
#, elixir-autogen, elixir-format
msgid "Latitude"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:534
+#: lib/who_need_help_web/live/activity_live/show.ex:541
#, elixir-autogen, elixir-format
msgid "Leave activity"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1025
+#: lib/who_need_help_web/live/request_live/show.ex:1038
#, 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:503
+#: lib/who_need_help_web/live/activity_live/show.ex:507
#, elixir-autogen, elixir-format
msgid "Location privacy"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:914
+#: lib/who_need_help_web/live/request_live/show.ex:927
#, 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:312
+#: lib/who_need_help_web/live/request_live/new.ex:317
#, elixir-autogen, elixir-format
msgid "Location visibility"
msgstr ""
@@ -1008,12 +999,12 @@ msgstr ""
msgid "Location-supported people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:717
+#: lib/who_need_help_web/live/request_live/show.ex:722
#, elixir-autogen, elixir-format
msgid "Location:"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:136
#, elixir-autogen, elixir-format
msgid "Log in and stay logged in"
msgstr ""
@@ -1023,23 +1014,18 @@ msgstr ""
msgid "Log in instructions"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:139
#, elixir-autogen, elixir-format
msgid "Log in only this time"
msgstr ""
-#: 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_controller.ex:117
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:289
-#: lib/who_need_help_web/live/request_live/new.ex:347
+#: lib/who_need_help_web/live/activity_live/new.ex:294
+#: lib/who_need_help_web/live/request_live/new.ex:352
#, elixir-autogen, elixir-format
msgid "Longitude"
msgstr ""
@@ -1059,7 +1045,7 @@ msgstr ""
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:591
+#: lib/who_need_help_web/live/activity_live/show.ex:598
#, elixir-autogen, elixir-format
msgid "Mark completed"
msgstr ""
@@ -1071,12 +1057,12 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:637
#, elixir-autogen, elixir-format
msgid "Matched"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:113
+#: lib/who_need_help_web/live/request_live/new.ex:118
#, elixir-autogen, elixir-format
msgid "Medicine is ready at the pharmacy"
msgstr ""
@@ -1091,7 +1077,7 @@ msgstr ""
msgid "Medicine pickup"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:156
+#: lib/who_need_help_web/live/request_live/new.ex:161
#, 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 ""
@@ -1106,7 +1092,7 @@ msgstr ""
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:386
+#: lib/who_need_help_web/live/activity_live/show.ex:390
#, elixir-autogen, elixir-format
msgid "Meeting area"
msgstr ""
@@ -1126,12 +1112,12 @@ msgstr ""
msgid "Merge note"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:483
+#: lib/who_need_help_web/live/activity_live/show.ex:487
#, elixir-autogen, elixir-format
msgid "Message the approved group"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:97
+#: lib/who_need_help_web/live/category_proposal_live.ex:103
#, elixir-autogen, elixir-format
msgid "Mode"
msgstr ""
@@ -1165,7 +1151,7 @@ msgstr ""
msgid "Nearby help"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:116
+#: lib/who_need_help_web/live/request_live/new.ex:121
#, elixir-autogen, elixir-format
msgid "Need roadside help in a safe location"
msgstr ""
@@ -1175,15 +1161,15 @@ msgstr ""
msgid "Network"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:94
#, elixir-autogen, elixir-format
msgid "New password"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:212
+#: lib/who_need_help_web/live/activity_live/show.ex:300
+#: lib/who_need_help_web/live/request_live/new.ex:260
+#: lib/who_need_help_web/live/request_live/show.ex:568
#, elixir-autogen, elixir-format
msgid "No"
msgstr ""
@@ -1193,8 +1179,8 @@ msgstr ""
msgid "No completed help yet."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:451
-#: lib/who_need_help_web/live/request_live/show.ex:771
+#: lib/who_need_help_web/live/activity_live/show.ex:455
+#: lib/who_need_help_web/live/request_live/show.ex:776
#, elixir-autogen, elixir-format
msgid "No messages yet."
msgstr ""
@@ -1214,12 +1200,12 @@ msgstr ""
msgid "No open signals."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:586
+#: lib/who_need_help_web/live/activity_live/show.ex:593
#, elixir-autogen, elixir-format
msgid "No pending join requests."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:134
+#: lib/who_need_help_web/live/category_proposal_live.ex:140
#, elixir-autogen, elixir-format
msgid "No proposals yet."
msgstr ""
@@ -1252,9 +1238,9 @@ msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:168
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:298
+#: lib/who_need_help_web/live/request_live/show.ex:628
+#: lib/who_need_help_web/live/request_live/show.ex:629
#, elixir-autogen, elixir-format
msgid "Now"
msgstr ""
@@ -1279,17 +1265,17 @@ msgstr ""
msgid "Only the conversation linked to this report is displayed."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:437
+#: lib/who_need_help_web/live/activity_live/show.ex:441
#, 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:166
-#: lib/who_need_help_web/live/activity_live/show.ex:338
+#: lib/who_need_help_web/live/activity_live/show.ex:342
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:636
#, elixir-autogen, elixir-format
msgid "Open"
msgstr ""
@@ -1299,7 +1285,7 @@ msgstr ""
msgid "Open abuse signals"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:128
+#: lib/who_need_help_web/live/category_proposal_live.ex:134
#, elixir-autogen, elixir-format
msgid "Open proposals"
msgstr ""
@@ -1309,50 +1295,50 @@ msgstr ""
msgid "Optional external thank-you link"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1011
+#: lib/who_need_help_web/live/request_live/show.ex:1024
#, elixir-autogen, elixir-format
msgid "Optional thank-you link"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:390
+#: lib/who_need_help_web/live/activity_live/show.ex:394
#, elixir-autogen, elixir-format
msgid "Organizer"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:155
+#: lib/who_need_help_web/live/activity_live/show.ex:159
#, elixir-autogen, elixir-format
msgid "Organizer blocked. Their activities and messages are hidden."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:539
+#: lib/who_need_help_web/live/activity_live/show.ex:546
#, elixir-autogen, elixir-format
msgid "Organizer controls"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:399
+#: lib/who_need_help_web/live/activity_live/show.ex:403
#, elixir-autogen, elixir-format
msgid "Organizer social links"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:667
+#: lib/who_need_help_web/live/activity_live/show.ex:679
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:1110
#, elixir-autogen, elixir-format
msgid "Other"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:112
#, elixir-autogen, elixir-format
msgid "Parent (optional)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:459
+#: lib/who_need_help_web/live/activity_live/show.ex:463
#, elixir-autogen, elixir-format
msgid "Participant"
msgstr ""
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:130
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
@@ -1362,27 +1348,27 @@ msgstr ""
msgid "Password updated successfully."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:280
+#: lib/who_need_help_web/live/request_live/new.ex:285
#, elixir-autogen, elixir-format
msgid "Pickup instructions (optional)"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:686
+#: lib/who_need_help_web/live/request_live/show.ex:691
#, elixir-autogen, elixir-format
msgid "Pickup notes"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:225
+#: lib/who_need_help_web/live/activity_live/new.ex:230
#, elixir-autogen, elixir-format
msgid "Plan and expectations"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:421
+#: lib/who_need_help_web/live/activity_live/show.ex:425
#, elixir-autogen, elixir-format
msgid "Plan details"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:619
+#: lib/who_need_help_web/live/request_live/show.ex:624
#, elixir-autogen, elixir-format
msgid "Please check the entered data."
msgstr ""
@@ -1392,12 +1378,12 @@ msgstr ""
msgid "Please check the submitted fields."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:334
+#: lib/who_need_help_web/live/activity_live/show.ex:338
#, elixir-autogen, elixir-format
msgid "Please check the submitted message."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:307
+#: lib/who_need_help_web/live/request_live/new.ex:312
#, elixir-autogen, elixir-format
msgid "Podil, near the central pharmacy"
msgstr ""
@@ -1413,7 +1399,7 @@ msgstr ""
msgid "Primary navigation"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:755
+#: lib/who_need_help_web/live/request_live/show.ex:760
#, elixir-autogen, elixir-format
msgid "Private match chat"
msgstr ""
@@ -1429,7 +1415,7 @@ msgid "Profile updated."
msgstr ""
#: lib/who_need_help_web/live/moderation_live.ex:251
-#: lib/who_need_help_web/live/request_live/show.ex:1089
+#: lib/who_need_help_web/live/request_live/show.ex:1107
#, elixir-autogen, elixir-format
msgid "Prohibited item"
msgstr ""
@@ -1449,12 +1435,12 @@ msgstr ""
msgid "Proposal rejected."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:86
+#: lib/who_need_help_web/live/category_proposal_live.ex:87
#, elixir-autogen, elixir-format
msgid "Proposals and votes guide moderators. Approval remains a human decision."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:91
+#: lib/who_need_help_web/live/category_proposal_live.ex:97
#, elixir-autogen, elixir-format
msgid "Proposed category"
msgstr ""
@@ -1464,7 +1450,7 @@ 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:256
+#: lib/who_need_help_web/live/activity_live/new.ex:261
#, elixir-autogen, elixir-format
msgid "Public map visibility"
msgstr ""
@@ -1479,28 +1465,28 @@ msgstr ""
msgid "Public reputation"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:117
+#: lib/who_need_help_web/live/activity_live/new.ex:122
#, 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:310
+#: lib/who_need_help_web/live/activity_live/new.ex:315
#, elixir-autogen, elixir-format
msgid "Publish activity"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:123
+#: lib/who_need_help_web/live/category_proposal_live.ex:129
#, elixir-autogen, elixir-format
msgid "Publish proposal"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:364
+#: lib/who_need_help_web/live/request_live/new.ex:369
#, elixir-autogen, elixir-format
msgid "Publish request"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:308
-#: lib/who_need_help_web/live/request_live/new.ex:362
+#: lib/who_need_help_web/live/activity_live/new.ex:313
+#: lib/who_need_help_web/live/request_live/new.ex:367
#, elixir-autogen, elixir-format
msgid "Publishing…"
msgstr ""
@@ -1526,17 +1512,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:823
+#: lib/who_need_help_web/live/request_live/show.ex:836
#, elixir-autogen, elixir-format
msgid "Rating"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:77
#, elixir-autogen, elixir-format
msgid "Read the"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:660
+#: lib/who_need_help_web/live/activity_live/show.ex:672
#, elixir-autogen, elixir-format
msgid "Reason"
msgstr ""
@@ -1561,22 +1547,22 @@ msgstr ""
msgid "Remove"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:468
+#: lib/who_need_help_web/live/activity_live/show.ex:472
#, elixir-autogen, elixir-format
msgid "Report"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:789
+#: lib/who_need_help_web/live/request_live/show.ex:794
#, elixir-autogen, elixir-format
msgid "Report message"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1084
+#: lib/who_need_help_web/live/request_live/show.ex:1102
#, elixir-autogen, elixir-format
msgid "Report reason"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:123
+#: lib/who_need_help_web/live/activity_live/show.ex:126
#: lib/who_need_help_web/live/request_live/show.ex:261
#, elixir-autogen, elixir-format
msgid "Report sent to moderators."
@@ -1597,7 +1583,7 @@ msgstr ""
msgid "Reports"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1053
+#: lib/who_need_help_web/live/request_live/show.ex:1066
#, elixir-autogen, elixir-format
msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages."
msgstr ""
@@ -1607,7 +1593,7 @@ msgstr ""
msgid "Request cancelled."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:301
+#: lib/who_need_help_web/live/request_live/new.ex:306
#, elixir-autogen, elixir-format
msgid "Request expires (UTC)"
msgstr ""
@@ -1622,22 +1608,22 @@ msgstr ""
msgid "Request restored."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:521
+#: lib/who_need_help_web/live/activity_live/show.ex:525
#, elixir-autogen, elixir-format
msgid "Request to join"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:725
+#: lib/who_need_help_web/live/request_live/show.ex:730
#, elixir-autogen, elixir-format
msgid "Requester links:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:706
+#: lib/who_need_help_web/live/request_live/show.ex:711
#, elixir-autogen, elixir-format
msgid "Requester:"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:745
#, elixir-autogen, elixir-format
msgid "Requester: %{count} completed"
msgstr ""
@@ -1665,6 +1651,7 @@ msgid "Review note"
msgstr ""
#: lib/who_need_help_web/live/request_live/show.ex:233
+#: lib/who_need_help_web/live/request_live/show.ex:825
#, elixir-autogen, elixir-format
msgid "Review saved. It appears after both participants review."
msgstr ""
@@ -1696,8 +1683,8 @@ msgstr ""
msgid "Russian name (optional)"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:635
-#: lib/who_need_help_web/live/request_live/show.ex:1051
+#: lib/who_need_help_web/live/activity_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:1064
#, elixir-autogen, elixir-format
msgid "Safety controls"
msgstr ""
@@ -1720,7 +1707,7 @@ msgstr ""
msgid "Save"
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:48
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:108
#, elixir-autogen, elixir-format
msgid "Save Password"
msgstr ""
@@ -1733,21 +1720,21 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:300
+#: lib/who_need_help_web/live/request_live/show.ex:632
+#: lib/who_need_help_web/live/request_live/show.ex:633
#, elixir-autogen, elixir-format
msgid "Scheduled"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:486
-#: lib/who_need_help_web/live/request_live/show.ex:806
+#: lib/who_need_help_web/live/activity_live/show.ex:490
+#: lib/who_need_help_web/live/request_live/show.ex:811
#, elixir-autogen, elixir-format
msgid "Send"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:676
-#: lib/who_need_help_web/live/request_live/show.ex:1101
+#: lib/who_need_help_web/live/activity_live/show.ex:688
+#: lib/who_need_help_web/live/request_live/show.ex:1119
#, elixir-autogen, elixir-format
msgid "Send report"
msgstr ""
@@ -1757,12 +1744,12 @@ 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:1038
+#: lib/who_need_help_web/live/request_live/show.ex:1051
#, elixir-autogen, elixir-format
msgid "Share location"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:282
+#: lib/who_need_help_web/live/request_live/new.ex:287
#, elixir-autogen, elixir-format
msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat."
msgstr ""
@@ -1772,8 +1759,8 @@ msgstr ""
msgid "Short bio"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:219
-#: lib/who_need_help_web/live/request_live/new.ex:266
+#: lib/who_need_help_web/live/activity_live/new.ex:224
+#: lib/who_need_help_web/live/request_live/new.ex:271
#, elixir-autogen, elixir-format
msgid "Short title"
msgstr ""
@@ -1803,8 +1790,8 @@ msgstr ""
msgid "Social activities"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:100
-#: lib/who_need_help_web/live/category_proposal_live.ex:143
+#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:149
#, elixir-autogen, elixir-format
msgid "Social activity"
msgstr ""
@@ -1844,24 +1831,24 @@ msgstr ""
msgid "Social verification session is invalid."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:665
+#: lib/who_need_help_web/live/activity_live/show.ex:677
#: lib/who_need_help_web/live/moderation_live.ex:252
-#: lib/who_need_help_web/live/request_live/show.ex:1090
+#: lib/who_need_help_web/live/request_live/show.ex:1108
#, elixir-autogen, elixir-format
msgid "Spam"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:940
+#: lib/who_need_help_web/live/request_live/show.ex:953
#, elixir-autogen, elixir-format
msgid "Start helping"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:374
+#: lib/who_need_help_web/live/activity_live/show.ex:378
#, elixir-autogen, elixir-format
msgid "Starts"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:234
+#: lib/who_need_help_web/live/activity_live/new.ex:239
#, elixir-autogen, elixir-format
msgid "Starts at (UTC)"
msgstr ""
@@ -1871,7 +1858,7 @@ msgstr ""
msgid "Status"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1046
+#: lib/who_need_help_web/live/request_live/show.ex:1059
#, elixir-autogen, elixir-format
msgid "Stop and delete position"
msgstr ""
@@ -1886,7 +1873,7 @@ msgstr ""
msgid "Structured fields must be a valid JSON object."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:837
+#: lib/who_need_help_web/live/request_live/show.ex:850
#, elixir-autogen, elixir-format
msgid "Submit review"
msgstr ""
@@ -1897,13 +1884,13 @@ msgstr ""
msgid "Suspended"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:604
+#: lib/who_need_help_web/live/request_live/show.ex:609
#, elixir-autogen, elixir-format
msgid "That handover code is not correct."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:140
-#: lib/who_need_help_web/live/request_live/show.ex:278
+#: lib/who_need_help_web/live/activity_live/show.ex:144
+#: lib/who_need_help_web/live/request_live/show.ex:279
#, elixir-autogen, elixir-format
msgid "That message cannot be reported from this view."
msgstr ""
@@ -1913,7 +1900,7 @@ msgstr ""
msgid "That social account is already linked."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:608
+#: lib/who_need_help_web/live/request_live/show.ex:613
#, elixir-autogen, elixir-format
msgid "That step is not available in the current state."
msgstr ""
@@ -1928,7 +1915,7 @@ msgstr ""
msgid "That verification provider is not supported."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:312
+#: lib/who_need_help_web/live/activity_live/show.ex:316
#, elixir-autogen, elixir-format
msgid "The approved group has reached its capacity."
msgstr ""
@@ -1948,7 +1935,7 @@ msgstr ""
msgid "The link is invalid or it has expired."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:526
+#: lib/who_need_help_web/live/activity_live/show.ex:530
#, elixir-autogen, elixir-format
msgid "The organizer has not approved your request yet."
msgstr ""
@@ -1963,48 +1950,48 @@ msgstr ""
msgid "The platform never handles, requires, splits, or guarantees a payment."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:508
+#: lib/who_need_help_web/live/activity_live/show.ex:512
#, 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:648
+#: lib/who_need_help_web/live/activity_live/show.ex:660
#, 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:1076
+#: lib/who_need_help_web/live/request_live/show.ex:1094
#, 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:315
+#: lib/who_need_help_web/live/activity_live/show.ex:319
#, elixir-autogen, elixir-format
msgid "This action is unavailable."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:322
+#: lib/who_need_help_web/live/activity_live/show.ex:326
#, elixir-autogen, elixir-format
msgid "This activity is no longer open."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:320
+#: lib/who_need_help_web/live/activity_live/show.ex:324
#, elixir-autogen, elixir-format
msgid "This activity state has already changed."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:613
+#: lib/who_need_help_web/live/request_live/show.ex:618
#, elixir-autogen, elixir-format
msgid "This interaction is blocked."
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:210
-#: lib/who_need_help_web/live/request_live/show.ex:665
+#: lib/who_need_help_web/live/request_live/show.ex:670
#, 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:164
+#: lib/who_need_help_web/live/request_live/new.ex:169
#, 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 ""
@@ -2014,7 +2001,7 @@ msgstr ""
msgid "This proposal has already been reviewed."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:56
+#: lib/who_need_help_web/live/category_proposal_live.ex:57
#, elixir-autogen, elixir-format
msgid "This proposal is already closed."
msgstr ""
@@ -2024,13 +2011,13 @@ msgstr ""
msgid "This report has no linked conversation."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1016
+#: lib/who_need_help_web/live/request_live/show.ex:1029
#, elixir-autogen, elixir-format
msgid "This request already has a helper."
msgstr ""
#: lib/who_need_help_web/live/request_live/show.ex:74
-#: lib/who_need_help_web/live/request_live/show.ex:615
+#: lib/who_need_help_web/live/request_live/show.ex:620
#, elixir-autogen, elixir-format
msgid "This request is no longer available."
msgstr ""
@@ -2048,28 +2035,29 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:299
+#: lib/who_need_help_web/live/request_live/show.ex:630
+#: lib/who_need_help_web/live/request_live/show.ex:631
#, elixir-autogen, elixir-format
msgid "Today"
msgstr ""
-#: 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/activity_live/new.ex:108
+#: lib/who_need_help_web/live/activity_live/show.ex:336
+#: lib/who_need_help_web/live/category_proposal_live.ex:55
#: lib/who_need_help_web/live/profile_live.ex:59
-#: lib/who_need_help_web/live/request_live/show.ex:618
+#: lib/who_need_help_web/live/request_live/show.ex:623
#, elixir-autogen, elixir-format
msgid "Too many actions in the configured time window."
msgstr ""
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:180
#: 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:142
+#: lib/who_need_help_web/live/request_live/new.ex:147
#, elixir-autogen, elixir-format
msgid "Too many requests in the configured time window."
msgstr ""
@@ -2079,7 +2067,7 @@ msgstr ""
msgid "Too many sign-in emails in the configured time window."
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:107
+#: lib/who_need_help_web/live/category_proposal_live.ex:113
#, elixir-autogen, elixir-format
msgid "Top level"
msgstr ""
@@ -2089,7 +2077,7 @@ msgstr ""
msgid "Total"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:246
+#: lib/who_need_help_web/live/activity_live/new.ex:251
#, elixir-autogen, elixir-format
msgid "Total group capacity"
msgstr ""
@@ -2109,7 +2097,7 @@ msgstr ""
msgid "Unblock"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:1069
+#: lib/who_need_help_web/live/request_live/show.ex:1082
#, elixir-autogen, elixir-format
msgid "Unblock this user"
msgstr ""
@@ -2125,18 +2113,18 @@ msgid "Update email instructions"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:240
-#: lib/who_need_help_web/live/request_live/new.ex:291
+#: lib/who_need_help_web/live/request_live/new.ex:296
#, elixir-autogen, elixir-format
msgid "Urgency"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:99
-#: lib/who_need_help_web/live/category_proposal_live.ex:142
+#: lib/who_need_help_web/live/category_proposal_live.ex:105
+#: lib/who_need_help_web/live/category_proposal_live.ex:148
#, elixir-autogen, elixir-format
msgid "Urgent help"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:123
+#: lib/who_need_help_web/live/activity_live/new.ex:128
#, 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 ""
@@ -2151,8 +2139,8 @@ msgstr ""
msgid "Use light theme"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:271
-#: lib/who_need_help_web/live/request_live/new.ex:329
+#: lib/who_need_help_web/live/activity_live/new.ex:276
+#: lib/who_need_help_web/live/request_live/new.ex:334
#, elixir-autogen, elixir-format
msgid "Use my current foreground location"
msgstr ""
@@ -2174,7 +2162,7 @@ msgstr ""
msgid "User"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:307
+#: lib/who_need_help_web/live/request_live/show.ex:308
#, elixir-autogen, elixir-format
msgid "User blocked. Their requests and new messages are hidden."
msgstr ""
@@ -2195,7 +2183,7 @@ msgid "User status updated."
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:87
-#: lib/who_need_help_web/live/request_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:322
#, elixir-autogen, elixir-format
msgid "User unblocked."
msgstr ""
@@ -2210,7 +2198,7 @@ msgstr ""
msgid "Verify GitHub"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:996
+#: lib/who_need_help_web/live/request_live/show.ex:1009
#, elixir-autogen, elixir-format
msgid "Verify handover"
msgstr ""
@@ -2230,11 +2218,12 @@ msgstr ""
msgid "Visible reviews"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:866
+#: lib/who_need_help_web/live/request_live/show.ex:879
#, elixir-autogen, elixir-format
msgid "Waiting for a nearby volunteer."
msgstr ""
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:137
#: 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
@@ -2246,18 +2235,18 @@ msgstr ""
msgid "What Codex contributed"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:673
-#: lib/who_need_help_web/live/request_live/show.ex:1098
+#: lib/who_need_help_web/live/activity_live/show.ex:685
+#: lib/who_need_help_web/live/request_live/show.ex:1116
#, elixir-autogen, elixir-format
msgid "What happened?"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:83
+#: lib/who_need_help_web/live/category_proposal_live.ex:84
#, elixir-autogen, elixir-format
msgid "What help category is missing?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:272
+#: lib/who_need_help_web/live/request_live/new.ex:277
#, elixir-autogen, elixir-format
msgid "What help do you need?"
msgstr ""
@@ -2277,7 +2266,7 @@ msgstr ""
msgid "Who Need Help home"
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:90
#, 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 ""
@@ -2292,40 +2281,40 @@ msgstr ""
msgid "Who needs help?"
msgstr ""
-#: lib/who_need_help_web/live/category_proposal_live.ex:121
+#: lib/who_need_help_web/live/category_proposal_live.ex:127
#, elixir-autogen, elixir-format
msgid "Why is this useful?"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:957
+#: lib/who_need_help_web/live/request_live/show.ex:970
#, elixir-autogen, elixir-format
msgid "Withdraw from this request"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:802
+#: lib/who_need_help_web/live/request_live/show.ex:807
#, elixir-autogen, elixir-format
msgid "Write a safe coordination message…"
msgstr ""
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:209
+#: lib/who_need_help_web/live/activity_live/show.ex:298
+#: lib/who_need_help_web/live/request_live/new.ex:257
+#: lib/who_need_help_web/live/request_live/show.ex:566
#, elixir-autogen, elixir-format
msgid "Yes"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:309
+#: lib/who_need_help_web/live/activity_live/show.ex:313
#, 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:58
+#: lib/who_need_help_web/live/category_proposal_live.ex:59
#, elixir-autogen, elixir-format
msgid "You already voted for this proposal."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:506
+#: lib/who_need_help_web/live/activity_live/show.ex:510
#, elixir-autogen, elixir-format
msgid "You are approved, so the map can show the exact meeting coordinates."
msgstr ""
@@ -2335,8 +2324,8 @@ msgstr ""
msgid "You are matched. Use chat to coordinate safely."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:317
-#: lib/who_need_help_web/live/request_live/show.ex:605
+#: lib/who_need_help_web/live/activity_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:610
#, elixir-autogen, elixir-format
msgid "You are not allowed to do that."
msgstr ""
@@ -2346,7 +2335,7 @@ msgstr ""
msgid "You are running the local mail adapter."
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:332
+#: lib/who_need_help_web/live/request_live/new.ex:337
#, elixir-autogen, elixir-format
msgid "You can also enter coordinates manually for local testing."
msgstr ""
@@ -2356,12 +2345,12 @@ msgstr ""
msgid "You can be the first person to ask the community."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:602
+#: lib/who_need_help_web/live/request_live/show.ex:607
#, elixir-autogen, elixir-format
msgid "You cannot accept your own request."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:316
+#: lib/who_need_help_web/live/activity_live/show.ex:320
#, elixir-autogen, elixir-format
msgid "You cannot report your own content."
msgstr ""
@@ -2413,7 +2402,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:47
+#: lib/who_need_help_web/live/request_live/new.ex:52
#, elixir-autogen, elixir-format
msgid "Your request is now visible to nearby helpers."
msgstr ""
@@ -2423,7 +2412,7 @@ msgstr ""
msgid "Your requests"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:817
+#: lib/who_need_help_web/live/request_live/show.ex:822
#, elixir-autogen, elixir-format
msgid "Your review is revealed only after both participants submit."
msgstr ""
@@ -2443,13 +2432,13 @@ msgstr ""
msgid "activity message"
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:79
#, elixir-autogen, elixir-format
msgid "before confirming."
msgstr ""
#: 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/category_proposal_live.ex:153
#: 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
@@ -2458,7 +2447,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:896
#, elixir-autogen, elixir-format
msgid "completed"
msgstr ""
@@ -2473,59 +2462,54 @@ msgstr ""
msgid "for an account now."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:910
+#: lib/who_need_help_web/live/request_live/show.ex:923
#, elixir-autogen, elixir-format
msgid "helper movement supported"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:911
+#: lib/who_need_help_web/live/request_live/show.ex:924
#, elixir-autogen, elixir-format
msgid "no movement evidence"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:902
+#: lib/who_need_help_web/live/request_live/show.ex:915
#, elixir-autogen, elixir-format
msgid "no proximity evidence"
msgstr ""
-#: 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:605
+#: lib/who_need_help_web/live/activity_live/show.ex:612
#, elixir-autogen, elixir-format
msgid "organizer"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:756
+#: lib/who_need_help_web/live/request_live/show.ex:761
#, elixir-autogen, elixir-format
msgid "participants only"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:328
-#: lib/who_need_help_web/live/request_live/show.ex:887
+#: lib/who_need_help_web/live/request_live/show.ex:900
#, elixir-autogen, elixir-format
msgid "people"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:901
+#: lib/who_need_help_web/live/request_live/show.ex:914
#, elixir-autogen, elixir-format
msgid "proximity supported"
msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:204
-#: lib/who_need_help_web/live/request_live/show.ex:891
+#: lib/who_need_help_web/live/request_live/show.ex:904
#, elixir-autogen, elixir-format
msgid "rating"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:748
+#: lib/who_need_help_web/live/request_live/show.ex:753
#, elixir-autogen, elixir-format
msgid "rating %{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:78
#, elixir-autogen, elixir-format
msgid "safety rules"
msgstr ""
@@ -2545,12 +2529,12 @@ msgstr ""
msgid "unique people"
msgstr ""
-#: 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/activity_live/show.ex:418
+#: lib/who_need_help_web/live/activity_live/show.ex:572
+#: lib/who_need_help_web/live/activity_live/show.ex:631
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:945
#, elixir-autogen, elixir-format
msgid "unverified"
msgstr ""
@@ -2560,13 +2544,13 @@ msgstr ""
msgid "urgent"
msgstr ""
-#: 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/activity_live/show.ex:417
+#: lib/who_need_help_web/live/activity_live/show.ex:571
+#: lib/who_need_help_web/live/activity_live/show.ex:630
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:739
+#: lib/who_need_help_web/live/request_live/show.ex:944
#, elixir-autogen, elixir-format
msgid "verified"
msgstr ""
@@ -2576,18 +2560,18 @@ msgstr ""
msgid "verified handovers"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:660
+#: lib/who_need_help_web/live/request_live/show.ex:665
#, elixir-autogen, elixir-format
msgid "← All requests"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/new.ex:113
-#: lib/who_need_help_web/live/activity_live/show.ex:348
+#: lib/who_need_help_web/live/activity_live/new.ex:118
+#: lib/who_need_help_web/live/activity_live/show.ex:352
#, elixir-autogen, elixir-format
msgid "← Back to activities"
msgstr ""
-#: lib/who_need_help_web/live/request_live/new.ex:152
+#: lib/who_need_help_web/live/request_live/new.ex:157
#, elixir-autogen, elixir-format
msgid "← Back to requests"
msgstr ""
@@ -2648,15 +2632,15 @@ msgstr ""
msgid "Unusual action rate"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:448
-#: lib/who_need_help_web/live/request_live/show.ex:768
+#: lib/who_need_help_web/live/activity_live/show.ex:452
+#: lib/who_need_help_web/live/request_live/show.ex:773
#, elixir-autogen, elixir-format
msgid "Load earlier messages"
msgstr ""
#: 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/category_proposal_live.ex:168
#: lib/who_need_help_web/live/leaderboard_live.ex:87
#: lib/who_need_help_web/live/moderation_live.ex:384
#: lib/who_need_help_web/live/moderation_live.ex:419
@@ -2671,9 +2655,9 @@ msgid "Load more"
msgstr ""
#: 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/activity_live/show.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:314
-#: lib/who_need_help_web/live/request_live/show.ex:849
+#: lib/who_need_help_web/live/request_live/show.ex:862
#, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable."
msgstr ""
@@ -2694,19 +2678,19 @@ msgstr ""
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
+#: lib/who_need_help_web/live/activity_live/show.ex:340
+#: lib/who_need_help_web/live/category_proposal_live.ex:61
+#: lib/who_need_help_web/live/request_live/show.ex:626
#, 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
+#: lib/who_need_help_web/live/activity_live/new.ex:110
#, 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
+#: lib/who_need_help_web/live/request_live/new.ex:149
#, elixir-autogen, elixir-format
msgid "Could not publish the request. Please try again."
msgstr ""
@@ -2721,32 +2705,32 @@ msgstr ""
msgid "JavaScript is required to confirm this protected email link."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:594
+#: lib/who_need_help_web/live/request_live/show.ex:599
#, elixir-autogen, elixir-format
msgid "Last update: %{time}"
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:617
+#: lib/who_need_help_web/live/request_live/show.ex:622
#, elixir-autogen, elixir-format
msgid "Live location sharing is not active."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:592
+#: lib/who_need_help_web/live/request_live/show.ex:597
#, elixir-autogen, elixir-format
msgid "Shared location"
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:329
+#: lib/who_need_help_web/live/activity_live/show.ex:333
#, elixir-autogen, elixir-format
msgid "The organizer cannot leave the activity."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:326
+#: lib/who_need_help_web/live/activity_live/show.ex:330
#, elixir-autogen, elixir-format
msgid "The organizer is already attending."
msgstr ""
-#: lib/who_need_help_web/controllers/user_registration_controller.ex:60
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:58
#, elixir-autogen, elixir-format
msgid "The registration form is invalid."
msgstr ""
@@ -2766,22 +2750,22 @@ msgstr ""
msgid "The sign-in form is invalid."
msgstr ""
-#: lib/who_need_help_web/live/activity_live/show.ex:323
+#: lib/who_need_help_web/live/activity_live/show.ex:327
#, elixir-autogen, elixir-format
msgid "This activity is no longer available."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:616
+#: lib/who_need_help_web/live/request_live/show.ex:621
#, elixir-autogen, elixir-format
msgid "This match is no longer active."
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 "This proposal is no longer available."
msgstr ""
-#: lib/who_need_help_web/live/request_live/show.ex:614
+#: lib/who_need_help_web/live/request_live/show.ex:619
#, elixir-autogen, elixir-format
msgid "This request has expired."
msgstr ""
@@ -2796,12 +2780,12 @@ msgstr ""
msgid "Your secure email link is ready. Continue to sign in."
msgstr ""
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:128
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:130
#, 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
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:107
#, 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."
@@ -2815,3 +2799,185 @@ msgstr ""
#, 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."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:32
+#, elixir-autogen, elixir-format
+msgid "A Google account is connected. You can use it to sign in."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:207
+#, elixir-autogen, elixir-format
+msgid "A different Google account is already connected."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:17
+#, elixir-autogen, elixir-format
+msgid "A password is not required. We will email a one-time confirmation link. You can add a password later in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:170
+#, elixir-autogen, elixir-format
+msgid "An account with this email already exists. Sign in by email or password, then connect Google in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:37
+#, elixir-autogen, elixir-format
+msgid "Confirm that you are 18 or older and accept the safety rules first."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:49
+#, elixir-autogen, elixir-format
+msgid "Connect Google account"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#, elixir-autogen, elixir-format
+msgid "Connect Google only after signing in here. We never merge accounts automatically by matching email addresses."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "Create account and email me a link"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:99
+#, elixir-autogen, elixir-format
+msgid "Email me a sign-in link"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:215
+#, elixir-autogen, elixir-format
+msgid "Google account connection session is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:97
+#, elixir-autogen, elixir-format
+msgid "Google did not provide a verified email address."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:29
+#, elixir-autogen, elixir-format
+msgid "Google sign-in"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:197
+#, elixir-autogen, elixir-format
+msgid "Google sign-in connected."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:123
+#, elixir-autogen, elixir-format
+msgid "Google sign-in could not be started."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:102
+#, elixir-autogen, elixir-format
+msgid "Google sign-in failed. Please try again."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:128
+#, elixir-autogen, elixir-format
+msgid "Google sign-in is not configured yet."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:87
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session expired. Please start again."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:91
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:47
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:53
+#, elixir-autogen, elixir-format
+msgid "Google sign-in will be available after the operator configures it."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:95
+#, elixir-autogen, elixir-format
+msgid "Sending link..."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:74
+#, elixir-autogen, elixir-format
+msgid "Sign in with Google"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43
+#, elixir-autogen, elixir-format
+msgid "Sign up with Google"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:202
+#, elixir-autogen, elixir-format
+msgid "That Google account belongs to another local account."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:45
+#, elixir-autogen, elixir-format, fuzzy
+msgid "The Google registration form is invalid."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:260
+#, elixir-autogen, elixir-format
+msgid "This Google account cannot be used right now."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:144
+#, elixir-autogen, elixir-format
+msgid "This Google account is not connected yet. Create an account or sign in by email and connect Google in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:109
+#, elixir-autogen, elixir-format
+msgid "This works only if you previously added a password in account settings."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#, elixir-autogen, elixir-format
+msgid "Use a password instead"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "We will send a one-time sign-in link. No password is required."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:163
+#, elixir-autogen, elixir-format
+msgid "Your account was created with Google."
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:51
+#, elixir-autogen, elixir-format, fuzzy
+msgid "or sign up with email"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82
+#, elixir-autogen, elixir-format
+msgid "or use email"
+msgstr ""
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:70
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google"
+msgstr "Disconnect Google"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:68
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google sign-in from this account?"
+msgstr "Disconnect Google sign-in from this account?"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:60
+#, elixir-autogen, elixir-format
+msgid "Google sign-in disconnected."
+msgstr "Google sign-in disconnected."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:65
+#, elixir-autogen, elixir-format
+msgid "No Google account is connected."
+msgstr "No Google account is connected."
diff --git a/priv/gettext/ru/LC_MESSAGES/default.po b/priv/gettext/ru/LC_MESSAGES/default.po
index 749fc6b..2bd4d4c 100644
--- a/priv/gettext/ru/LC_MESSAGES/default.po
+++ b/priv/gettext/ru/LC_MESSAGES/default.po
@@ -17,7 +17,7 @@ msgstr ""
msgid "Account settings"
msgstr "Настройки аккаунта"
-#: lib/who_need_help_web/components/core_components.ex:393
+#: lib/who_need_help_web/components/core_components.ex:446
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr "Действия"
@@ -25,7 +25,7 @@ msgstr "Действия"
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:23
#, elixir-autogen, elixir-format
msgid "Ask for help"
msgstr "Попросить о помощи"
@@ -108,7 +108,7 @@ msgstr "Нет соединения с интернетом"
msgid "close"
msgstr "закрыть"
-#: lib/who_need_help_web/live/activity_live/show.ex:363
+#: lib/who_need_help_web/live/activity_live/show.ex:367
#, elixir-autogen, elixir-format
msgid "%{approved}/%{capacity} approved"
msgstr "Одобрено: %{approved}/%{capacity}"
@@ -118,37 +118,37 @@ msgstr "Одобрено: %{approved}/%{capacity}"
msgid "%{count} community votes"
msgstr "Голосов сообщества: %{count}"
-#: lib/who_need_help_web/live/request_live/show.ex:745
+#: lib/who_need_help_web/live/request_live/show.ex:750
#, elixir-autogen, elixir-format
msgid "%{count} unique people"
msgstr "%{count} уникальных людей"
-#: lib/who_need_help_web/live/request_live/show.ex:829
+#: lib/who_need_help_web/live/request_live/show.ex:842
#, elixir-autogen, elixir-format
msgid "1 — unsafe"
msgstr "1 — небезопасно"
-#: lib/who_need_help_web/live/request_live/show.ex:828
+#: lib/who_need_help_web/live/request_live/show.ex:841
#, elixir-autogen, elixir-format
msgid "2 — poor"
msgstr "2 — плохо"
-#: lib/who_need_help_web/live/request_live/show.ex:827
+#: lib/who_need_help_web/live/request_live/show.ex:840
#, elixir-autogen, elixir-format
msgid "3 — okay"
msgstr "3 — нормально"
-#: lib/who_need_help_web/live/request_live/show.ex:826
+#: lib/who_need_help_web/live/request_live/show.ex:839
#, elixir-autogen, elixir-format
msgid "4 — good"
msgstr "4 — хорошо"
-#: lib/who_need_help_web/live/request_live/show.ex:825
+#: lib/who_need_help_web/live/request_live/show.ex:838
#, elixir-autogen, elixir-format
msgid "5 — excellent"
msgstr "5 — отлично"
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:118
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:120
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
@@ -162,13 +162,13 @@ msgstr ""
"Сигнал требует проверки человеком; отсутствие необязательного отслеживания "
"не является доказательством злоупотребления."
-#: lib/who_need_help_web/live/request_live/show.ex:859
+#: lib/who_need_help_web/live/request_live/show.ex:872
#, 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:633
+#: lib/who_need_help_web/live/request_live/show.ex:638
#, elixir-autogen, elixir-format
msgid "Accepted"
msgstr "Принято"
@@ -189,7 +189,7 @@ msgstr "Аккаунт и язык"
msgid "Accounts"
msgstr "Аккаунты"
-#: lib/who_need_help_web/live/request_live/show.ex:855
+#: lib/who_need_help_web/live/request_live/show.ex:868
#, elixir-autogen, elixir-format
msgid "Action center"
msgstr "Центр действий"
@@ -217,7 +217,7 @@ msgstr ""
"походов согласуйте маршрут и меры безопасности и никогда не публикуйте "
"домашние адреса, коды доступа или документы для поездки."
-#: lib/who_need_help_web/live/activity_live/new.ex:163
+#: lib/who_need_help_web/live/activity_live/new.ex:168
#, elixir-autogen, elixir-format
msgid "Activity details"
msgstr "Детали активности"
@@ -228,12 +228,12 @@ msgid "Activity hidden."
msgstr "Активность скрыта."
#: lib/who_need_help_web/live/activity_live/show.ex:18
-#: lib/who_need_help_web/live/activity_live/show.ex:177
+#: lib/who_need_help_web/live/activity_live/show.ex:181
#, elixir-autogen, elixir-format
msgid "Activity is unavailable."
msgstr "Активность недоступна."
-#: lib/who_need_help_web/live/activity_live/new.ex:36
+#: lib/who_need_help_web/live/activity_live/new.ex:41
#, elixir-autogen, elixir-format
msgid "Activity published. You approve every participant."
msgstr "Активность опубликована. Вы одобряете каждого участника."
@@ -244,7 +244,7 @@ msgid "Activity restored."
msgstr "Активность восстановлена."
#: lib/who_need_help_web/live/activity_live/index.ex:210
-#: lib/who_need_help_web/live/activity_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Activity type"
msgstr "Тип активности"
@@ -275,7 +275,7 @@ msgstr "Все категории"
msgid "Already registered?"
msgstr "Уже зарегистрированы?"
-#: lib/who_need_help_web/live/request_live/show.ex:603
+#: lib/who_need_help_web/live/request_live/show.ex:608
#, elixir-autogen, elixir-format
msgid "Another helper already accepted this request."
msgstr "Другой помощник уже принял эту заявку."
@@ -285,60 +285,60 @@ msgstr "Другой помощник уже принял эту заявку."
msgid "Any urgency"
msgstr "Любая срочность"
-#: lib/who_need_help_web/live/activity_live/show.ex:524
+#: lib/who_need_help_web/live/activity_live/show.ex:528
#, elixir-autogen, elixir-format
msgid "Approval pending"
msgstr "Ожидает одобрения"
-#: lib/who_need_help_web/live/activity_live/show.ex:574
+#: lib/who_need_help_web/live/activity_live/show.ex:581
#, elixir-autogen, elixir-format
msgid "Approve"
msgstr "Одобрить"
-#: lib/who_need_help_web/live/activity_live/show.ex:435
+#: lib/who_need_help_web/live/activity_live/show.ex:439
#, elixir-autogen, elixir-format
msgid "Approved group chat"
msgstr "Чат одобренной группы"
-#: lib/who_need_help_web/live/activity_live/show.ex:600
+#: lib/who_need_help_web/live/activity_live/show.ex:607
#, 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:258
-#: lib/who_need_help_web/live/request_live/new.ex:306
+#: lib/who_need_help_web/live/activity_live/new.ex:263
+#: lib/who_need_help_web/live/request_live/new.ex:311
#, elixir-autogen, elixir-format
msgid "Approximate area"
msgstr "Примерный район"
-#: lib/who_need_help_web/live/activity_live/new.ex:250
+#: lib/who_need_help_web/live/activity_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Approximate public meeting area"
msgstr "Примерный публичный район встречи"
#: lib/who_need_help_web/live/profile_live.ex:167
-#: lib/who_need_help_web/live/request_live/show.ex:639
+#: lib/who_need_help_web/live/request_live/show.ex:644
#, elixir-autogen, elixir-format
msgid "Approximate publicly"
msgstr "Публично — приблизительно"
-#: lib/who_need_help_web/live/request_live/new.ex:314
+#: lib/who_need_help_web/live/request_live/new.ex:319
#, elixir-autogen, elixir-format
msgid "Approximate publicly (recommended)"
msgstr "Показывать приблизительно (рекомендуется)"
-#: lib/who_need_help_web/live/request_live/show.ex:702
+#: lib/who_need_help_web/live/request_live/show.ex:707
#, elixir-autogen, elixir-format
msgid "Area:"
msgstr "Район:"
-#: lib/who_need_help_web/live/request_live/new.ex:154
+#: lib/who_need_help_web/live/request_live/new.ex:159
#, elixir-autogen, elixir-format
msgid "Ask for urgent help"
msgstr "Попросить срочную помощь"
-#: lib/who_need_help_web/live/category_proposal_live.ex:92
+#: lib/who_need_help_web/live/category_proposal_live.ex:98
#, elixir-autogen, elixir-format
msgid "Bicycle puncture"
msgstr "Прокол колеса велосипеда"
@@ -358,12 +358,12 @@ msgstr ""
msgid "Block and report"
msgstr "Блокировка и жалоба"
-#: lib/who_need_help_web/live/activity_live/show.ex:641
+#: lib/who_need_help_web/live/activity_live/show.ex:648
#, elixir-autogen, elixir-format
msgid "Block organizer"
msgstr "Заблокировать организатора"
-#: lib/who_need_help_web/live/request_live/show.ex:1062
+#: lib/who_need_help_web/live/request_live/show.ex:1075
#, elixir-autogen, elixir-format
msgid "Block this user"
msgstr "Заблокировать пользователя"
@@ -379,7 +379,7 @@ msgid "Both confirm and the helper enters a one-time code."
msgstr ""
"Оба участника подтверждают завершение, а помощник вводит одноразовый код."
-#: lib/who_need_help_web/live/request_live/new.ex:118
+#: lib/who_need_help_web/live/request_live/new.ex:123
#, elixir-autogen, elixir-format
msgid "Briefly describe the help you need"
msgstr "Кратко опишите, какая помощь вам нужна"
@@ -399,33 +399,33 @@ msgstr "Отчёт о разработке"
msgid "COMMUNITY TRUST"
msgstr "ДОВЕРИЕ СООБЩЕСТВА"
-#: lib/who_need_help_web/live/category_proposal_live.ex:80
+#: lib/who_need_help_web/live/category_proposal_live.ex:81
#, elixir-autogen, elixir-format
msgid "COMMUNITY-DRIVEN TAXONOMY"
msgstr "КАТЕГОРИИ ОТ СООБЩЕСТВА"
-#: lib/who_need_help_web/live/activity_live/show.ex:594
+#: lib/who_need_help_web/live/activity_live/show.ex:601
#, elixir-autogen, elixir-format
msgid "Cancel activity"
msgstr "Отменить активность"
-#: lib/who_need_help_web/live/request_live/show.ex:873
-#: lib/who_need_help_web/live/request_live/show.ex:967
+#: lib/who_need_help_web/live/request_live/show.ex:886
+#: lib/who_need_help_web/live/request_live/show.ex:980
#, elixir-autogen, elixir-format
msgid "Cancel request"
msgstr "Отменить заявку"
#: 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/activity_live/show.ex:344
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:641
#, elixir-autogen, elixir-format
msgid "Cancelled"
msgstr "Отменено"
#: lib/who_need_help_web/live/request_live/index.ex:225
-#: lib/who_need_help_web/live/request_live/new.ex:180
+#: lib/who_need_help_web/live/request_live/new.ex:185
#, elixir-autogen, elixir-format
msgid "Category"
msgstr "Категория"
@@ -435,13 +435,13 @@ msgstr "Категория"
msgid "Category created and proposal approved."
msgstr "Категория создана и предложение одобрено."
-#: lib/who_need_help_web/live/request_live/new.ex:211
-#: lib/who_need_help_web/live/request_live/show.ex:691
+#: lib/who_need_help_web/live/request_live/new.ex:216
+#: lib/who_need_help_web/live/request_live/show.ex:696
#, 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/category_proposal_live.ex:67
#: lib/who_need_help_web/live/moderation_live.ex:466
#, elixir-autogen, elixir-format
msgid "Category proposals"
@@ -458,7 +458,7 @@ msgid "Change role"
msgstr "Изменить роль"
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:21
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:47
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:107
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr "Изменение…"
@@ -469,31 +469,31 @@ msgid "Choose a category, urgency, and safe location visibility."
msgstr ""
"Выберите категорию, срочность и безопасный уровень видимости геопозиции."
-#: lib/who_need_help_web/live/activity_live/new.ex:140
+#: lib/who_need_help_web/live/activity_live/new.ex:145
#, elixir-autogen, elixir-format
msgid "Choose activity"
msgstr "Выберите активность"
-#: lib/who_need_help_web/live/request_live/new.ex:181
+#: lib/who_need_help_web/live/request_live/new.ex:186
#, elixir-autogen, elixir-format
msgid "Choose category"
msgstr "Выберите категорию"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:180
+#: lib/who_need_help_web/live/activity_live/new.ex:207
+#: lib/who_need_help_web/live/request_live/new.ex:228
+#: lib/who_need_help_web/live/request_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Choose…"
msgstr "Выберите…"
-#: lib/who_need_help_web/live/activity_live/new.ex:251
+#: lib/who_need_help_web/live/activity_live/new.ex:256
#, elixir-autogen, elixir-format
msgid "City centre, near the main entrance"
msgstr "Центр города, рядом с главным входом"
-#: lib/who_need_help_web/live/activity_live/show.ex:654
-#: lib/who_need_help_web/live/request_live/show.ex:1078
+#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/request_live/show.ex:1096
#, elixir-autogen, elixir-format
msgid "Clear"
msgstr "Очистить"
@@ -503,7 +503,7 @@ msgstr "Очистить"
msgid "Close"
msgstr "Закрыть"
-#: lib/who_need_help_web/live/activity_live/new.ex:220
+#: lib/who_need_help_web/live/activity_live/new.ex:225
#, elixir-autogen, elixir-format
msgid "Coffee and conversation"
msgstr "Кофе и разговоры"
@@ -515,7 +515,7 @@ msgstr ""
"Кофе, кино, прогулки и походы отделены от срочной помощи и никогда не влияют"
" на рейтинг помощника."
-#: lib/who_need_help_web/live/request_live/show.ex:835
+#: lib/who_need_help_web/live/request_live/show.ex:848
#, elixir-autogen, elixir-format
msgid "Comment (optional)"
msgstr "Комментарий (необязательно)"
@@ -526,19 +526,19 @@ msgid "Community helpers"
msgstr "Помощники сообщества"
#: 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/activity_live/show.ex:396
+#: lib/who_need_help_web/live/activity_live/show.ex:553
+#: lib/who_need_help_web/live/activity_live/show.ex:610
#: 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:167
-#: lib/who_need_help_web/live/activity_live/show.ex:339
+#: lib/who_need_help_web/live/activity_live/show.ex:343
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:640
#, elixir-autogen, elixir-format
msgid "Completed"
msgstr "Завершено"
@@ -553,24 +553,24 @@ msgstr "Подтвердить и войти только сейчас"
msgid "Confirm and stay logged in"
msgstr "Подтвердить и оставаться в системе"
-#: lib/who_need_help_web/live/request_live/show.ex:947
+#: lib/who_need_help_web/live/request_live/show.ex:960
#, elixir-autogen, elixir-format
msgid "Confirm my part is complete"
msgstr "Подтвердить выполнение своей части"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:42
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:102
#, elixir-autogen, elixir-format
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:611
+#: lib/who_need_help_web/live/category_proposal_live.ex:52
+#: lib/who_need_help_web/live/request_live/show.ex:616
#, 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:100
-#: lib/who_need_help_web/live/request_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:105
+#: lib/who_need_help_web/live/request_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Confirm your email and ensure your account is active before publishing."
msgstr ""
@@ -595,17 +595,12 @@ msgid "Create a clear plan in a public place."
msgstr "Создайте четкий план в общественном месте."
#: lib/who_need_help_web/live/activity_live/index.ex:188
-#: lib/who_need_help_web/live/activity_live/new.ex:11
+#: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format
msgid "Create activity"
msgstr "Создать активность"
-#: 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:115
+#: lib/who_need_help_web/live/activity_live/new.ex:120
#, elixir-autogen, elixir-format
msgid "Create an activity"
msgstr "Создать активность"
@@ -620,18 +615,13 @@ msgstr "Создать категорию"
msgid "Create urgent request"
msgstr "Создать срочную заявку"
-#: 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:663
+#: lib/who_need_help_web/live/activity_live/show.ex:675
#, elixir-autogen, elixir-format
msgid "Dangerous plan"
msgstr "Опасный план"
#: lib/who_need_help_web/live/moderation_live.ex:248
-#: lib/who_need_help_web/live/request_live/show.ex:1086
+#: lib/who_need_help_web/live/request_live/show.ex:1104
#, elixir-autogen, elixir-format
msgid "Dangerous request"
msgstr "Опасный запрос"
@@ -653,7 +643,7 @@ msgstr ""
"Решения и доступ к материалам жалоб на переписку записываются в журнал "
"аудита."
-#: lib/who_need_help_web/live/activity_live/show.ex:581
+#: lib/who_need_help_web/live/activity_live/show.ex:588
#, elixir-autogen, elixir-format
msgid "Decline"
msgstr "Отклонить"
@@ -663,7 +653,7 @@ msgstr "Отклонить"
msgid "Default location visibility"
msgstr "Видимость местоположения по умолчанию"
-#: lib/who_need_help_web/live/activity_live/new.ex:227
+#: lib/who_need_help_web/live/activity_live/new.ex:232
#, elixir-autogen, elixir-format
msgid "Describe the public plan, approximate duration, and who might enjoy it."
msgstr ""
@@ -677,7 +667,7 @@ msgstr ""
msgid "Dismissed"
msgstr "Отклонено"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:71
#: lib/who_need_help_web/live/profile_live.ex:149
#, elixir-autogen, elixir-format
msgid "Display name"
@@ -696,14 +686,14 @@ msgstr ""
msgid "Don't have an account?"
msgstr "Нет аккаунта?"
-#: lib/who_need_help_web/live/request_live/show.ex:815
+#: lib/who_need_help_web/live/request_live/show.ex:820
#, 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:71
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:62
#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:122
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15
#, elixir-autogen, elixir-format
msgid "Email"
@@ -745,7 +735,7 @@ msgstr "Описание на английском"
msgid "English name"
msgstr "Название на английском"
-#: lib/who_need_help_web/live/request_live/show.ex:992
+#: lib/who_need_help_web/live/request_live/show.ex:1005
#, elixir-autogen, elixir-format
msgid "Enter handover code"
msgstr "Введите код передачи"
@@ -755,7 +745,7 @@ msgstr "Введите код передачи"
msgid "Exact address after matching"
msgstr "Точный адрес после назначения помощника"
-#: lib/who_need_help_web/live/activity_live/new.ex:274
+#: lib/who_need_help_web/live/activity_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Exact coordinates are shown only to participants you approve."
msgstr "Точные координаты показаны только участникам, которых вы одобряете."
@@ -765,29 +755,29 @@ msgstr "Точные координаты показаны только учас
msgid "Exact for active match"
msgstr "Точно — для активной заявки"
-#: lib/who_need_help_web/live/request_live/new.ex:316
-#: lib/who_need_help_web/live/request_live/show.ex:641
+#: lib/who_need_help_web/live/request_live/new.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:646
#, elixir-autogen, elixir-format
msgid "Exact only for active match"
msgstr "Точно только для активной заявки"
#: lib/who_need_help_web/live/profile_live.ex:169
-#: lib/who_need_help_web/live/request_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:647
#, elixir-autogen, elixir-format
msgid "Exact publicly"
msgstr "Публично — точно"
-#: lib/who_need_help_web/live/request_live/new.ex:317
+#: lib/who_need_help_web/live/request_live/new.ex:322
#, elixir-autogen, elixir-format
msgid "Exact publicly — explicit consent"
msgstr "Публично — точно, с явным согласием"
-#: lib/who_need_help_web/live/request_live/show.ex:710
+#: lib/who_need_help_web/live/request_live/show.ex:715
#, elixir-autogen, elixir-format
msgid "Expires:"
msgstr "Истекает:"
-#: lib/who_need_help_web/live/request_live/new.ex:274
+#: lib/who_need_help_web/live/request_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "Объясните, что уже сделано и что нужно сделать волонтеру."
@@ -797,9 +787,9 @@ msgstr "Объясните, что уже сделано и что нужно с
msgid "Find people to join"
msgstr "Найти компанию"
-#: lib/who_need_help_web/live/activity_live/show.ex:664
+#: lib/who_need_help_web/live/activity_live/show.ex:676
#: lib/who_need_help_web/live/moderation_live.ex:250
-#: lib/who_need_help_web/live/request_live/show.ex:1088
+#: lib/who_need_help_web/live/request_live/show.ex:1106
#, elixir-autogen, elixir-format
msgid "Fraud"
msgstr "Мошенничество"
@@ -821,7 +811,7 @@ msgstr ""
"Подтверждение через GitHub недоступно, пока оператор не настроит учётные "
"данные OAuth. Ссылка, введённая вручную, не считается подтверждённой."
-#: lib/who_need_help_web/live/request_live/show.ex:975
+#: lib/who_need_help_web/live/request_live/show.ex:988
#, elixir-autogen, elixir-format
msgid "HANDOVER CODE FOR THE HELPER"
msgstr "КОД ПЕРЕДАЧИ ДЛЯ ПОМОЩНИКА"
@@ -836,9 +826,9 @@ msgstr "Имя пользователя (необязательно)"
msgid "Handover code verified."
msgstr "Код передачи подтверждён."
-#: lib/who_need_help_web/live/activity_live/show.ex:662
+#: lib/who_need_help_web/live/activity_live/show.ex:674
#: lib/who_need_help_web/live/moderation_live.ex:249
-#: lib/who_need_help_web/live/request_live/show.ex:1087
+#: lib/who_need_help_web/live/request_live/show.ex:1105
#, elixir-autogen, elixir-format
msgid "Harassment"
msgstr "Домогательства"
@@ -862,7 +852,7 @@ msgstr ""
msgid "Helper"
msgstr "Помощник"
-#: lib/who_need_help_web/live/request_live/show.ex:878
+#: lib/who_need_help_web/live/request_live/show.ex:891
#, elixir-autogen, elixir-format
msgid "Helper: %{name}"
msgstr "Помощник: %{name}"
@@ -913,10 +903,10 @@ msgstr ""
"\n"
"Если вы не запрашивали это письмо, просто проигнорируйте его."
-#: lib/who_need_help_web/live/activity_live/new.ex:259
+#: lib/who_need_help_web/live/activity_live/new.ex:264
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:320
+#: lib/who_need_help_web/live/request_live/show.ex:645
#, elixir-autogen, elixir-format
msgid "Hidden"
msgstr "Скрыто"
@@ -942,18 +932,19 @@ msgstr "Скрыть заявку"
msgid "Human decisions retained"
msgstr "Решения остаются за человеком"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:39
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:85
#, 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:862
+#: lib/who_need_help_web/live/request_live/show.ex:875
#, elixir-autogen, elixir-format
msgid "I can help"
msgstr "Я могу помочь"
-#: lib/who_need_help_web/live/request_live/new.ex:127
+#: lib/who_need_help_web/live/request_live/new.ex:132
#, 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 ""
@@ -961,7 +952,7 @@ msgstr ""
"находятся вне активного движения либо экстренные службы уже вызваны, а "
"запрашиваемая помощь законна."
-#: lib/who_need_help_web/live/request_live/new.ex:121
+#: lib/who_need_help_web/live/request_live/new.ex:126
#, 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 ""
@@ -969,14 +960,14 @@ msgstr ""
"консультации или контролируемого вещества и не просьба купить лекарство от "
"моего имени."
-#: lib/who_need_help_web/live/request_live/new.ex:133
+#: lib/who_need_help_web/live/request_live/new.ex:138
#, 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:298
+#: lib/who_need_help_web/live/activity_live/new.ex:303
#, 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 ""
@@ -996,14 +987,14 @@ msgstr ""
"Если этот адрес электронной почты есть в нашей системе, вскоре вы получите "
"инструкции для входа."
-#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/activity_live/show.ex:678
#: lib/who_need_help_web/live/moderation_live.ex:253
-#: lib/who_need_help_web/live/request_live/show.ex:1091
+#: lib/who_need_help_web/live/request_live/show.ex:1109
#, elixir-autogen, elixir-format
msgid "Impersonation"
msgstr "Выдача себя за другого человека"
-#: lib/who_need_help_web/live/request_live/show.ex:1027
+#: lib/who_need_help_web/live/request_live/show.ex:1040
#, 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 ""
@@ -1013,7 +1004,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:639
#, elixir-autogen, elixir-format
msgid "In progress"
msgstr "В процессе"
@@ -1033,51 +1024,51 @@ msgstr "Внутренняя заметка"
msgid "Invalid email or password"
msgstr "Неверная электронная почта или пароль"
-#: lib/who_need_help_web/live/activity_live/show.ex:314
+#: lib/who_need_help_web/live/activity_live/show.ex:318
#, elixir-autogen, elixir-format
msgid "Join requests are closed."
msgstr "Приём заявок на участие закрыт."
-#: lib/who_need_help_web/live/activity_live/show.ex:380
+#: lib/who_need_help_web/live/activity_live/show.ex:384
#, elixir-autogen, elixir-format
msgid "Join requests close"
msgstr "Приём заявок завершается"
-#: lib/who_need_help_web/live/activity_live/new.ex:239
+#: lib/who_need_help_web/live/activity_live/new.ex:244
#, elixir-autogen, elixir-format
msgid "Join requests close (UTC)"
msgstr "Завершение приёма заявок (UTC)"
-#: lib/who_need_help_web/live/activity_live/new.ex:282
-#: lib/who_need_help_web/live/request_live/new.ex:340
+#: lib/who_need_help_web/live/activity_live/new.ex:287
+#: lib/who_need_help_web/live/request_live/new.ex:345
#, elixir-autogen, elixir-format
msgid "Latitude"
msgstr "Широта"
-#: lib/who_need_help_web/live/activity_live/show.ex:534
+#: lib/who_need_help_web/live/activity_live/show.ex:541
#, elixir-autogen, elixir-format
msgid "Leave activity"
msgstr "Покинуть активность"
-#: lib/who_need_help_web/live/request_live/show.ex:1025
+#: lib/who_need_help_web/live/request_live/show.ex:1038
#, 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:503
+#: lib/who_need_help_web/live/activity_live/show.ex:507
#, elixir-autogen, elixir-format
msgid "Location privacy"
msgstr "Конфиденциальность геопозиции"
-#: lib/who_need_help_web/live/request_live/show.ex:914
+#: lib/who_need_help_web/live/request_live/show.ex:927
#, 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:312
+#: lib/who_need_help_web/live/request_live/new.ex:317
#, elixir-autogen, elixir-format
msgid "Location visibility"
msgstr "Видимость геопозиции"
@@ -1087,12 +1078,12 @@ msgstr "Видимость геопозиции"
msgid "Location-supported people"
msgstr "Люди с подтверждением геопозицией"
-#: lib/who_need_help_web/live/request_live/show.ex:717
+#: lib/who_need_help_web/live/request_live/show.ex:722
#, elixir-autogen, elixir-format
msgid "Location:"
msgstr "Геопозиция:"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:136
#, elixir-autogen, elixir-format
msgid "Log in and stay logged in"
msgstr "Войти и оставаться в системе"
@@ -1102,23 +1093,18 @@ msgstr "Войти и оставаться в системе"
msgid "Log in instructions"
msgstr "Инструкции для входа"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:139
#, elixir-autogen, elixir-format
msgid "Log in only this time"
msgstr "Войти только сейчас"
-#: 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_controller.ex:117
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr "Вы успешно вышли."
-#: lib/who_need_help_web/live/activity_live/new.ex:289
-#: lib/who_need_help_web/live/request_live/new.ex:347
+#: lib/who_need_help_web/live/activity_live/new.ex:294
+#: lib/who_need_help_web/live/request_live/new.ex:352
#, elixir-autogen, elixir-format
msgid "Longitude"
msgstr "Долгота"
@@ -1143,7 +1129,7 @@ msgstr ""
"подтверждения появляется только после авторизации у соответствующего "
"провайдера."
-#: lib/who_need_help_web/live/activity_live/show.ex:591
+#: lib/who_need_help_web/live/activity_live/show.ex:598
#, elixir-autogen, elixir-format
msgid "Mark completed"
msgstr "Отметить завершённой"
@@ -1155,12 +1141,12 @@ msgstr "Найдите помощника и согласуйте детали"
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:637
#, elixir-autogen, elixir-format
msgid "Matched"
msgstr "Помощник найден"
-#: lib/who_need_help_web/live/request_live/new.ex:113
+#: lib/who_need_help_web/live/request_live/new.ex:118
#, elixir-autogen, elixir-format
msgid "Medicine is ready at the pharmacy"
msgstr "Лекарство готово к получению в аптеке"
@@ -1175,7 +1161,7 @@ msgstr "Заказ лекарств готов в аптеке"
msgid "Medicine pickup"
msgstr "Доставка лекарств"
-#: lib/who_need_help_web/live/request_live/new.ex:156
+#: lib/who_need_help_web/live/request_live/new.ex:161
#, 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 ""
@@ -1196,7 +1182,7 @@ msgstr ""
"одобренной группе и уходите или отправляйте жалобу, если ситуация кажется "
"небезопасной."
-#: lib/who_need_help_web/live/activity_live/show.ex:386
+#: lib/who_need_help_web/live/activity_live/show.ex:390
#, elixir-autogen, elixir-format
msgid "Meeting area"
msgstr "Место встречи"
@@ -1216,12 +1202,12 @@ msgstr "Объединить с"
msgid "Merge note"
msgstr "Примечание к объединению"
-#: lib/who_need_help_web/live/activity_live/show.ex:483
+#: lib/who_need_help_web/live/activity_live/show.ex:487
#, elixir-autogen, elixir-format
msgid "Message the approved group"
msgstr "Написать одобренной группе"
-#: lib/who_need_help_web/live/category_proposal_live.ex:97
+#: lib/who_need_help_web/live/category_proposal_live.ex:103
#, elixir-autogen, elixir-format
msgid "Mode"
msgstr "Режим"
@@ -1255,7 +1241,7 @@ msgstr "ВЗАИМОПОМОЩЬ РЯДОМ"
msgid "Nearby help"
msgstr "Помощь рядом"
-#: lib/who_need_help_web/live/request_live/new.ex:116
+#: lib/who_need_help_web/live/request_live/new.ex:121
#, elixir-autogen, elixir-format
msgid "Need roadside help in a safe location"
msgstr "Нужна помощь на дороге в безопасном месте"
@@ -1265,15 +1251,15 @@ msgstr "Нужна помощь на дороге в безопасном мес
msgid "Network"
msgstr "Сеть"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:94
#, elixir-autogen, elixir-format
msgid "New password"
msgstr "Новый пароль"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:212
+#: lib/who_need_help_web/live/activity_live/show.ex:300
+#: lib/who_need_help_web/live/request_live/new.ex:260
+#: lib/who_need_help_web/live/request_live/show.ex:568
#, elixir-autogen, elixir-format
msgid "No"
msgstr "Нет"
@@ -1283,8 +1269,8 @@ msgstr "Нет"
msgid "No completed help yet."
msgstr "Завершённой помощи пока нет."
-#: lib/who_need_help_web/live/activity_live/show.ex:451
-#: lib/who_need_help_web/live/request_live/show.ex:771
+#: lib/who_need_help_web/live/activity_live/show.ex:455
+#: lib/who_need_help_web/live/request_live/show.ex:776
#, elixir-autogen, elixir-format
msgid "No messages yet."
msgstr "Сообщений пока нет."
@@ -1304,12 +1290,12 @@ msgstr "Открытых заявок пока нет"
msgid "No open signals."
msgstr "Открытых сигналов нет."
-#: lib/who_need_help_web/live/activity_live/show.ex:586
+#: lib/who_need_help_web/live/activity_live/show.ex:593
#, elixir-autogen, elixir-format
msgid "No pending join requests."
msgstr "Нет ожидающих заявок на участие."
-#: lib/who_need_help_web/live/category_proposal_live.ex:134
+#: lib/who_need_help_web/live/category_proposal_live.ex:140
#, elixir-autogen, elixir-format
msgid "No proposals yet."
msgstr "Предложений пока нет."
@@ -1344,9 +1330,9 @@ msgstr "Не для чрезвычайных ситуаций"
#: lib/who_need_help_web/live/request_live/index.ex:168
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:298
+#: lib/who_need_help_web/live/request_live/show.ex:628
+#: lib/who_need_help_web/live/request_live/show.ex:629
#, elixir-autogen, elixir-format
msgid "Now"
msgstr "Сейчас"
@@ -1377,7 +1363,7 @@ msgstr ""
msgid "Only the conversation linked to this report is displayed."
msgstr "Показана только переписка, связанная с этой жалобой."
-#: lib/who_need_help_web/live/activity_live/show.ex:437
+#: lib/who_need_help_web/live/activity_live/show.ex:441
#, elixir-autogen, elixir-format
msgid "Only the organizer and approved participants can read or send these messages."
msgstr ""
@@ -1385,11 +1371,11 @@ msgstr ""
"сообщения."
#: 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/activity_live/show.ex:342
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:636
#, elixir-autogen, elixir-format
msgid "Open"
msgstr "Открыта"
@@ -1399,7 +1385,7 @@ msgstr "Открыта"
msgid "Open abuse signals"
msgstr "Открытые сигналы о злоупотреблениях"
-#: lib/who_need_help_web/live/category_proposal_live.ex:128
+#: lib/who_need_help_web/live/category_proposal_live.ex:134
#, elixir-autogen, elixir-format
msgid "Open proposals"
msgstr "Открытые предложения"
@@ -1409,50 +1395,50 @@ msgstr "Открытые предложения"
msgid "Optional external thank-you link"
msgstr "Необязательная внешняя ссылка для благодарности"
-#: lib/who_need_help_web/live/request_live/show.ex:1011
+#: lib/who_need_help_web/live/request_live/show.ex:1024
#, elixir-autogen, elixir-format
msgid "Optional thank-you link"
msgstr "Необязательная ссылка для благодарности"
-#: lib/who_need_help_web/live/activity_live/show.ex:390
+#: lib/who_need_help_web/live/activity_live/show.ex:394
#, elixir-autogen, elixir-format
msgid "Organizer"
msgstr "Организатор"
-#: lib/who_need_help_web/live/activity_live/show.ex:155
+#: lib/who_need_help_web/live/activity_live/show.ex:159
#, elixir-autogen, elixir-format
msgid "Organizer blocked. Their activities and messages are hidden."
msgstr "Организатор заблокирован. Его активности и сообщения скрыты."
-#: lib/who_need_help_web/live/activity_live/show.ex:539
+#: lib/who_need_help_web/live/activity_live/show.ex:546
#, elixir-autogen, elixir-format
msgid "Organizer controls"
msgstr "Управление организатора"
-#: lib/who_need_help_web/live/activity_live/show.ex:399
+#: lib/who_need_help_web/live/activity_live/show.ex:403
#, elixir-autogen, elixir-format
msgid "Organizer social links"
msgstr "Ссылки организатора на соцсети"
-#: lib/who_need_help_web/live/activity_live/show.ex:667
+#: lib/who_need_help_web/live/activity_live/show.ex:679
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:1110
#, elixir-autogen, elixir-format
msgid "Other"
msgstr "Другое"
-#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:112
#, elixir-autogen, elixir-format
msgid "Parent (optional)"
msgstr "Родительская категория (необязательно)"
-#: lib/who_need_help_web/live/activity_live/show.ex:459
+#: lib/who_need_help_web/live/activity_live/show.ex:463
#, elixir-autogen, elixir-format
msgid "Participant"
msgstr "Участник"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:130
#, elixir-autogen, elixir-format
msgid "Password"
msgstr "Пароль"
@@ -1462,27 +1448,27 @@ msgstr "Пароль"
msgid "Password updated successfully."
msgstr "Пароль успешно обновлён."
-#: lib/who_need_help_web/live/request_live/new.ex:280
+#: lib/who_need_help_web/live/request_live/new.ex:285
#, elixir-autogen, elixir-format
msgid "Pickup instructions (optional)"
msgstr "Инструкции для получения (необязательно)"
-#: lib/who_need_help_web/live/request_live/show.ex:686
+#: lib/who_need_help_web/live/request_live/show.ex:691
#, elixir-autogen, elixir-format
msgid "Pickup notes"
msgstr "Инструкции по получению"
-#: lib/who_need_help_web/live/activity_live/new.ex:225
+#: lib/who_need_help_web/live/activity_live/new.ex:230
#, elixir-autogen, elixir-format
msgid "Plan and expectations"
msgstr "План и ожидания"
-#: lib/who_need_help_web/live/activity_live/show.ex:421
+#: lib/who_need_help_web/live/activity_live/show.ex:425
#, elixir-autogen, elixir-format
msgid "Plan details"
msgstr "Подробности плана"
-#: lib/who_need_help_web/live/request_live/show.ex:619
+#: lib/who_need_help_web/live/request_live/show.ex:624
#, elixir-autogen, elixir-format
msgid "Please check the entered data."
msgstr "Пожалуйста, проверьте введенные данные."
@@ -1492,12 +1478,12 @@ msgstr "Пожалуйста, проверьте введенные данные
msgid "Please check the submitted fields."
msgstr "Пожалуйста, проверьте представленные поля."
-#: lib/who_need_help_web/live/activity_live/show.ex:334
+#: lib/who_need_help_web/live/activity_live/show.ex:338
#, elixir-autogen, elixir-format
msgid "Please check the submitted message."
msgstr "Пожалуйста, проверьте отправленное сообщение."
-#: lib/who_need_help_web/live/request_live/new.ex:307
+#: lib/who_need_help_web/live/request_live/new.ex:312
#, elixir-autogen, elixir-format
msgid "Podil, near the central pharmacy"
msgstr "Подол, рядом с центральной аптекой"
@@ -1513,7 +1499,7 @@ msgstr "Опубликуйте понятную заявку"
msgid "Primary navigation"
msgstr "Основная навигация"
-#: lib/who_need_help_web/live/request_live/show.ex:755
+#: lib/who_need_help_web/live/request_live/show.ex:760
#, elixir-autogen, elixir-format
msgid "Private match chat"
msgstr "Приватный чат участников"
@@ -1531,7 +1517,7 @@ msgid "Profile updated."
msgstr "Профиль обновлён."
#: lib/who_need_help_web/live/moderation_live.ex:251
-#: lib/who_need_help_web/live/request_live/show.ex:1089
+#: lib/who_need_help_web/live/request_live/show.ex:1107
#, elixir-autogen, elixir-format
msgid "Prohibited item"
msgstr "Запрещённый предмет"
@@ -1551,14 +1537,14 @@ msgstr "Предложение опубликовано для голосова
msgid "Proposal rejected."
msgstr "Предложение отклонено."
-#: lib/who_need_help_web/live/category_proposal_live.ex:86
+#: lib/who_need_help_web/live/category_proposal_live.ex:87
#, elixir-autogen, elixir-format
msgid "Proposals and votes guide moderators. Approval remains a human decision."
msgstr ""
"Предложения и голоса помогают модераторам, но решение об одобрении всегда "
"принимает человек."
-#: lib/who_need_help_web/live/category_proposal_live.ex:91
+#: lib/who_need_help_web/live/category_proposal_live.ex:97
#, elixir-autogen, elixir-format
msgid "Proposed category"
msgstr "Предлагаемая категория"
@@ -1575,7 +1561,7 @@ msgstr ""
"одним участником другого; производные признаки движения и близости могут "
"сохраняться."
-#: lib/who_need_help_web/live/activity_live/new.ex:256
+#: lib/who_need_help_web/live/activity_live/new.ex:261
#, elixir-autogen, elixir-format
msgid "Public map visibility"
msgstr "Видимость на публичной карте"
@@ -1590,30 +1576,30 @@ msgstr "URL публичного профиля"
msgid "Public reputation"
msgstr "Публичная репутация"
-#: lib/who_need_help_web/live/activity_live/new.ex:117
+#: lib/who_need_help_web/live/activity_live/new.ex:122
#, 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:310
+#: lib/who_need_help_web/live/activity_live/new.ex:315
#, elixir-autogen, elixir-format
msgid "Publish activity"
msgstr "Опубликовать активность"
-#: lib/who_need_help_web/live/category_proposal_live.ex:123
+#: lib/who_need_help_web/live/category_proposal_live.ex:129
#, elixir-autogen, elixir-format
msgid "Publish proposal"
msgstr "Опубликовать предложение"
-#: lib/who_need_help_web/live/request_live/new.ex:364
+#: lib/who_need_help_web/live/request_live/new.ex:369
#, elixir-autogen, elixir-format
msgid "Publish request"
msgstr "Опубликовать заявку"
-#: lib/who_need_help_web/live/activity_live/new.ex:308
-#: lib/who_need_help_web/live/request_live/new.ex:362
+#: lib/who_need_help_web/live/activity_live/new.ex:313
+#: lib/who_need_help_web/live/request_live/new.ex:367
#, elixir-autogen, elixir-format
msgid "Publishing…"
msgstr "Публикация…"
@@ -1643,17 +1629,17 @@ msgstr ""
"показатель."
#: lib/who_need_help_web/live/leaderboard_live.ex:60
-#: lib/who_need_help_web/live/request_live/show.ex:823
+#: lib/who_need_help_web/live/request_live/show.ex:836
#, elixir-autogen, elixir-format
msgid "Rating"
msgstr "Оценка"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:77
#, elixir-autogen, elixir-format
msgid "Read the"
msgstr "Прочитайте"
-#: lib/who_need_help_web/live/activity_live/show.ex:660
+#: lib/who_need_help_web/live/activity_live/show.ex:672
#, elixir-autogen, elixir-format
msgid "Reason"
msgstr "Причина"
@@ -1678,22 +1664,22 @@ msgstr "Примечание об отклонении"
msgid "Remove"
msgstr "Удалить"
-#: lib/who_need_help_web/live/activity_live/show.ex:468
+#: lib/who_need_help_web/live/activity_live/show.ex:472
#, elixir-autogen, elixir-format
msgid "Report"
msgstr "Пожаловаться"
-#: lib/who_need_help_web/live/request_live/show.ex:789
+#: lib/who_need_help_web/live/request_live/show.ex:794
#, elixir-autogen, elixir-format
msgid "Report message"
msgstr "Пожаловаться на сообщение"
-#: lib/who_need_help_web/live/request_live/show.ex:1084
+#: lib/who_need_help_web/live/request_live/show.ex:1102
#, elixir-autogen, elixir-format
msgid "Report reason"
msgstr "Причина жалобы"
-#: lib/who_need_help_web/live/activity_live/show.ex:123
+#: lib/who_need_help_web/live/activity_live/show.ex:126
#: lib/who_need_help_web/live/request_live/show.ex:261
#, elixir-autogen, elixir-format
msgid "Report sent to moderators."
@@ -1714,7 +1700,7 @@ msgstr "Материалы жалобы"
msgid "Reports"
msgstr "Жалобы"
-#: lib/who_need_help_web/live/request_live/show.ex:1053
+#: lib/who_need_help_web/live/request_live/show.ex:1066
#, elixir-autogen, elixir-format
msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages."
msgstr ""
@@ -1726,7 +1712,7 @@ msgstr ""
msgid "Request cancelled."
msgstr "Заявка отменена."
-#: lib/who_need_help_web/live/request_live/new.ex:301
+#: lib/who_need_help_web/live/request_live/new.ex:306
#, elixir-autogen, elixir-format
msgid "Request expires (UTC)"
msgstr "Срок действия заявки (UTC)"
@@ -1741,22 +1727,22 @@ msgstr "Заявка скрыта."
msgid "Request restored."
msgstr "Заявка восстановлена."
-#: lib/who_need_help_web/live/activity_live/show.ex:521
+#: lib/who_need_help_web/live/activity_live/show.ex:525
#, elixir-autogen, elixir-format
msgid "Request to join"
msgstr "Подать заявку на участие"
-#: lib/who_need_help_web/live/request_live/show.ex:725
+#: lib/who_need_help_web/live/request_live/show.ex:730
#, elixir-autogen, elixir-format
msgid "Requester links:"
msgstr "Ссылки заявителя:"
-#: lib/who_need_help_web/live/request_live/show.ex:706
+#: lib/who_need_help_web/live/request_live/show.ex:711
#, elixir-autogen, elixir-format
msgid "Requester:"
msgstr "Заявитель:"
-#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:745
#, elixir-autogen, elixir-format
msgid "Requester: %{count} completed"
msgstr "Автор заявки: %{count} завершено"
@@ -1784,6 +1770,7 @@ msgid "Review note"
msgstr "Примечание к проверке"
#: lib/who_need_help_web/live/request_live/show.ex:233
+#: lib/who_need_help_web/live/request_live/show.ex:825
#, elixir-autogen, elixir-format
msgid "Review saved. It appears after both participants review."
msgstr ""
@@ -1816,8 +1803,8 @@ msgstr "Описание на русском (необязательно)"
msgid "Russian name (optional)"
msgstr "Название на русском (необязательно)"
-#: lib/who_need_help_web/live/activity_live/show.ex:635
-#: lib/who_need_help_web/live/request_live/show.ex:1051
+#: lib/who_need_help_web/live/activity_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:1064
#, elixir-autogen, elixir-format
msgid "Safety controls"
msgstr "Инструменты безопасности"
@@ -1843,7 +1830,7 @@ msgstr "Правила безопасности"
msgid "Save"
msgstr "Сохранить"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:48
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:108
#, elixir-autogen, elixir-format
msgid "Save Password"
msgstr "Сохранить пароль"
@@ -1856,21 +1843,21 @@ msgstr "Сохранить профиль"
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:300
+#: lib/who_need_help_web/live/request_live/show.ex:632
+#: lib/who_need_help_web/live/request_live/show.ex:633
#, elixir-autogen, elixir-format
msgid "Scheduled"
msgstr "Запланировано"
-#: lib/who_need_help_web/live/activity_live/show.ex:486
-#: lib/who_need_help_web/live/request_live/show.ex:806
+#: lib/who_need_help_web/live/activity_live/show.ex:490
+#: lib/who_need_help_web/live/request_live/show.ex:811
#, elixir-autogen, elixir-format
msgid "Send"
msgstr "Отправить"
-#: lib/who_need_help_web/live/activity_live/show.ex:676
-#: lib/who_need_help_web/live/request_live/show.ex:1101
+#: lib/who_need_help_web/live/activity_live/show.ex:688
+#: lib/who_need_help_web/live/request_live/show.ex:1119
#, elixir-autogen, elixir-format
msgid "Send report"
msgstr "Отправить жалобу"
@@ -1882,12 +1869,12 @@ msgstr ""
"Установите CODEX_SESSION_ID в среде развёртывания перед записью "
"демонстрации."
-#: lib/who_need_help_web/live/request_live/show.ex:1038
+#: lib/who_need_help_web/live/request_live/show.ex:1051
#, elixir-autogen, elixir-format
msgid "Share location"
msgstr "Поделиться геопозицией"
-#: lib/who_need_help_web/live/request_live/new.ex:282
+#: lib/who_need_help_web/live/request_live/new.ex:287
#, elixir-autogen, elixir-format
msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat."
msgstr ""
@@ -1899,8 +1886,8 @@ msgstr ""
msgid "Short bio"
msgstr "Кратко о себе"
-#: lib/who_need_help_web/live/activity_live/new.ex:219
-#: lib/who_need_help_web/live/request_live/new.ex:266
+#: lib/who_need_help_web/live/activity_live/new.ex:224
+#: lib/who_need_help_web/live/request_live/new.ex:271
#, elixir-autogen, elixir-format
msgid "Short title"
msgstr "Краткий заголовок"
@@ -1930,8 +1917,8 @@ msgstr "Слаг"
msgid "Social activities"
msgstr "Совместные активности"
-#: lib/who_need_help_web/live/category_proposal_live.ex:100
-#: lib/who_need_help_web/live/category_proposal_live.ex:143
+#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:149
#, elixir-autogen, elixir-format
msgid "Social activity"
msgstr "Совместная активность"
@@ -1971,24 +1958,24 @@ msgstr "Сеанс подтверждения соцсети завершилс
msgid "Social verification session is invalid."
msgstr "Сеанс социальной проверки недействителен."
-#: lib/who_need_help_web/live/activity_live/show.ex:665
+#: lib/who_need_help_web/live/activity_live/show.ex:677
#: lib/who_need_help_web/live/moderation_live.ex:252
-#: lib/who_need_help_web/live/request_live/show.ex:1090
+#: lib/who_need_help_web/live/request_live/show.ex:1108
#, elixir-autogen, elixir-format
msgid "Spam"
msgstr "Спам"
-#: lib/who_need_help_web/live/request_live/show.ex:940
+#: lib/who_need_help_web/live/request_live/show.ex:953
#, elixir-autogen, elixir-format
msgid "Start helping"
msgstr "Начать помогать"
-#: lib/who_need_help_web/live/activity_live/show.ex:374
+#: lib/who_need_help_web/live/activity_live/show.ex:378
#, elixir-autogen, elixir-format
msgid "Starts"
msgstr "Начало"
-#: lib/who_need_help_web/live/activity_live/new.ex:234
+#: lib/who_need_help_web/live/activity_live/new.ex:239
#, elixir-autogen, elixir-format
msgid "Starts at (UTC)"
msgstr "Начало (UTC)"
@@ -1998,7 +1985,7 @@ msgstr "Начало (UTC)"
msgid "Status"
msgstr "Статус"
-#: lib/who_need_help_web/live/request_live/show.ex:1046
+#: lib/who_need_help_web/live/request_live/show.ex:1059
#, elixir-autogen, elixir-format
msgid "Stop and delete position"
msgstr "Остановить и удалить геопозицию"
@@ -2013,7 +2000,7 @@ msgstr "Структурированные поля JSON"
msgid "Structured fields must be a valid JSON object."
msgstr "Структурированные поля должны быть действительным объектом JSON."
-#: lib/who_need_help_web/live/request_live/show.ex:837
+#: lib/who_need_help_web/live/request_live/show.ex:850
#, elixir-autogen, elixir-format
msgid "Submit review"
msgstr "Отправить отзыв"
@@ -2024,13 +2011,13 @@ msgstr "Отправить отзыв"
msgid "Suspended"
msgstr "Заблокирован"
-#: lib/who_need_help_web/live/request_live/show.ex:604
+#: lib/who_need_help_web/live/request_live/show.ex:609
#, elixir-autogen, elixir-format
msgid "That handover code is not correct."
msgstr "Неверный код передачи."
-#: lib/who_need_help_web/live/activity_live/show.ex:140
-#: lib/who_need_help_web/live/request_live/show.ex:278
+#: lib/who_need_help_web/live/activity_live/show.ex:144
+#: lib/who_need_help_web/live/request_live/show.ex:279
#, elixir-autogen, elixir-format
msgid "That message cannot be reported from this view."
msgstr "На это сообщение нельзя пожаловаться с этого экрана."
@@ -2040,7 +2027,7 @@ msgstr "На это сообщение нельзя пожаловаться с
msgid "That social account is already linked."
msgstr "Эта учётная запись соцсети уже привязана."
-#: lib/who_need_help_web/live/request_live/show.ex:608
+#: lib/who_need_help_web/live/request_live/show.ex:613
#, elixir-autogen, elixir-format
msgid "That step is not available in the current state."
msgstr "Этот шаг недоступен в текущем состоянии."
@@ -2055,7 +2042,7 @@ msgstr "Провайдер подтверждения не настроен."
msgid "That verification provider is not supported."
msgstr "Этот провайдер проверки не поддерживается."
-#: lib/who_need_help_web/live/activity_live/show.ex:312
+#: lib/who_need_help_web/live/activity_live/show.ex:316
#, elixir-autogen, elixir-format
msgid "The approved group has reached its capacity."
msgstr "Одобренная группа уже заполнена."
@@ -2075,7 +2062,7 @@ msgstr "Последний администратор не может пониз
msgid "The link is invalid or it has expired."
msgstr "Ссылка недействительна или просрочена."
-#: lib/who_need_help_web/live/activity_live/show.ex:526
+#: lib/who_need_help_web/live/activity_live/show.ex:530
#, elixir-autogen, elixir-format
msgid "The organizer has not approved your request yet."
msgstr "Организатор ещё не одобрил вашу заявку."
@@ -2093,52 +2080,52 @@ msgstr ""
"Платформа никогда не обрабатывает, не требует, не разделяет и не гарантирует"
" оплату."
-#: lib/who_need_help_web/live/activity_live/show.ex:508
+#: lib/who_need_help_web/live/activity_live/show.ex:512
#, 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:648
+#: lib/who_need_help_web/live/activity_live/show.ex:660
#, 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:1076
+#: lib/who_need_help_web/live/request_live/show.ex:1094
#, 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:315
+#: lib/who_need_help_web/live/activity_live/show.ex:319
#, elixir-autogen, elixir-format
msgid "This action is unavailable."
msgstr "Это действие недоступно."
-#: lib/who_need_help_web/live/activity_live/show.ex:322
+#: lib/who_need_help_web/live/activity_live/show.ex:326
#, elixir-autogen, elixir-format
msgid "This activity is no longer open."
msgstr "Эта активность больше не открыта."
-#: lib/who_need_help_web/live/activity_live/show.ex:320
+#: lib/who_need_help_web/live/activity_live/show.ex:324
#, elixir-autogen, elixir-format
msgid "This activity state has already changed."
msgstr "Состояние этой активности уже изменилось."
-#: lib/who_need_help_web/live/request_live/show.ex:613
+#: lib/who_need_help_web/live/request_live/show.ex:618
#, elixir-autogen, elixir-format
msgid "This interaction is blocked."
msgstr "Это взаимодействие заблокировано."
#: lib/who_need_help_web/live/request_live/index.ex:210
-#: lib/who_need_help_web/live/request_live/show.ex:665
+#: lib/who_need_help_web/live/request_live/show.ex:670
#, 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:164
+#: lib/who_need_help_web/live/request_live/new.ex:169
#, 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 ""
@@ -2151,7 +2138,7 @@ msgstr ""
msgid "This proposal has already been reviewed."
msgstr "Это предложение уже было рассмотрено."
-#: lib/who_need_help_web/live/category_proposal_live.ex:56
+#: lib/who_need_help_web/live/category_proposal_live.ex:57
#, elixir-autogen, elixir-format
msgid "This proposal is already closed."
msgstr "Это предложение уже закрыто."
@@ -2161,13 +2148,13 @@ msgstr "Это предложение уже закрыто."
msgid "This report has no linked conversation."
msgstr "У этой жалобы нет связанной переписки."
-#: lib/who_need_help_web/live/request_live/show.ex:1016
+#: lib/who_need_help_web/live/request_live/show.ex:1029
#, elixir-autogen, elixir-format
msgid "This request already has a helper."
msgstr "У этой заявки уже есть помощник."
#: lib/who_need_help_web/live/request_live/show.ex:74
-#: lib/who_need_help_web/live/request_live/show.ex:615
+#: lib/who_need_help_web/live/request_live/show.ex:620
#, elixir-autogen, elixir-format
msgid "This request is no longer available."
msgstr "Эта заявка больше недоступна."
@@ -2185,28 +2172,29 @@ msgstr "Чтобы посмотреть отправленные письма,
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:299
+#: lib/who_need_help_web/live/request_live/show.ex:630
+#: lib/who_need_help_web/live/request_live/show.ex:631
#, elixir-autogen, elixir-format
msgid "Today"
msgstr "Сегодня"
-#: 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/activity_live/new.ex:108
+#: lib/who_need_help_web/live/activity_live/show.ex:336
+#: lib/who_need_help_web/live/category_proposal_live.ex:55
#: lib/who_need_help_web/live/profile_live.ex:59
-#: lib/who_need_help_web/live/request_live/show.ex:618
+#: lib/who_need_help_web/live/request_live/show.ex:623
#, elixir-autogen, elixir-format
msgid "Too many actions in the configured time window."
msgstr "Слишком много действий за настроенный промежуток времени."
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:180
#: 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:142
+#: lib/who_need_help_web/live/request_live/new.ex:147
#, elixir-autogen, elixir-format
msgid "Too many requests in the configured time window."
msgstr "Слишком много запросов за настроенный промежуток времени."
@@ -2216,7 +2204,7 @@ msgstr "Слишком много запросов за настроенный
msgid "Too many sign-in emails in the configured time window."
msgstr "Слишком много писем для входа за настроенный промежуток времени."
-#: lib/who_need_help_web/live/category_proposal_live.ex:107
+#: lib/who_need_help_web/live/category_proposal_live.ex:113
#, elixir-autogen, elixir-format
msgid "Top level"
msgstr "Верхний уровень"
@@ -2226,7 +2214,7 @@ msgstr "Верхний уровень"
msgid "Total"
msgstr "Всего"
-#: lib/who_need_help_web/live/activity_live/new.ex:246
+#: lib/who_need_help_web/live/activity_live/new.ex:251
#, elixir-autogen, elixir-format
msgid "Total group capacity"
msgstr "Общий размер группы"
@@ -2246,7 +2234,7 @@ msgstr "Название на украинском (необязательно)"
msgid "Unblock"
msgstr "Разблокировать"
-#: lib/who_need_help_web/live/request_live/show.ex:1069
+#: lib/who_need_help_web/live/request_live/show.ex:1082
#, elixir-autogen, elixir-format
msgid "Unblock this user"
msgstr "Разблокировать этого пользователя"
@@ -2262,18 +2250,18 @@ msgid "Update email instructions"
msgstr "Инструкции для изменения электронной почты"
#: lib/who_need_help_web/live/request_live/index.ex:240
-#: lib/who_need_help_web/live/request_live/new.ex:291
+#: lib/who_need_help_web/live/request_live/new.ex:296
#, elixir-autogen, elixir-format
msgid "Urgency"
msgstr "Срочность"
-#: lib/who_need_help_web/live/category_proposal_live.ex:99
-#: lib/who_need_help_web/live/category_proposal_live.ex:142
+#: lib/who_need_help_web/live/category_proposal_live.ex:105
+#: lib/who_need_help_web/live/category_proposal_live.ex:148
#, elixir-autogen, elixir-format
msgid "Urgent help"
msgstr "Срочная помощь"
-#: lib/who_need_help_web/live/activity_live/new.ex:123
+#: lib/who_need_help_web/live/activity_live/new.ex:128
#, 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 ""
@@ -2291,8 +2279,8 @@ msgstr "Включить тёмную тему"
msgid "Use light theme"
msgstr "Включить светлую тему"
-#: lib/who_need_help_web/live/activity_live/new.ex:271
-#: lib/who_need_help_web/live/request_live/new.ex:329
+#: lib/who_need_help_web/live/activity_live/new.ex:276
+#: lib/who_need_help_web/live/request_live/new.ex:334
#, elixir-autogen, elixir-format
msgid "Use my current foreground location"
msgstr "Использовать мою текущую геопозицию"
@@ -2316,7 +2304,7 @@ msgstr "Использовать системную тему"
msgid "User"
msgstr "Пользователь"
-#: lib/who_need_help_web/live/request_live/show.ex:307
+#: lib/who_need_help_web/live/request_live/show.ex:308
#, elixir-autogen, elixir-format
msgid "User blocked. Their requests and new messages are hidden."
msgstr "Пользователь заблокирован. Его заявки и новые сообщения скрыты."
@@ -2337,7 +2325,7 @@ msgid "User status updated."
msgstr "Обновлен статус пользователя."
#: lib/who_need_help_web/live/profile_live.ex:87
-#: lib/who_need_help_web/live/request_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:322
#, elixir-autogen, elixir-format
msgid "User unblocked."
msgstr "Пользователь разблокирован."
@@ -2352,7 +2340,7 @@ msgstr "Подтверждённые люди"
msgid "Verify GitHub"
msgstr "Подтвердить через GitHub"
-#: lib/who_need_help_web/live/request_live/show.ex:996
+#: lib/who_need_help_web/live/request_live/show.ex:1009
#, elixir-autogen, elixir-format
msgid "Verify handover"
msgstr "Подтвердить передачу"
@@ -2372,11 +2360,12 @@ msgstr "Просмотреть связанные доказательства"
msgid "Visible reviews"
msgstr "Открытые отзывы"
-#: lib/who_need_help_web/live/request_live/show.ex:866
+#: lib/who_need_help_web/live/request_live/show.ex:879
#, elixir-autogen, elixir-format
msgid "Waiting for a nearby volunteer."
msgstr "Ожидаем помощника поблизости."
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:137
#: 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
@@ -2388,18 +2377,18 @@ msgstr "С возвращением!"
msgid "What Codex contributed"
msgstr "Что сделал Codex"
-#: lib/who_need_help_web/live/activity_live/show.ex:673
-#: lib/who_need_help_web/live/request_live/show.ex:1098
+#: lib/who_need_help_web/live/activity_live/show.ex:685
+#: lib/who_need_help_web/live/request_live/show.ex:1116
#, elixir-autogen, elixir-format
msgid "What happened?"
msgstr "Что случилось?"
-#: lib/who_need_help_web/live/category_proposal_live.ex:83
+#: lib/who_need_help_web/live/category_proposal_live.ex:84
#, elixir-autogen, elixir-format
msgid "What help category is missing?"
msgstr "Какая категория помощи отсутствует?"
-#: lib/who_need_help_web/live/request_live/new.ex:272
+#: lib/who_need_help_web/live/request_live/new.ex:277
#, elixir-autogen, elixir-format
msgid "What help do you need?"
msgstr "Какая помощь вам нужна?"
@@ -2425,7 +2414,7 @@ msgstr ""
msgid "Who Need Help home"
msgstr "Главная Who Need Help"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:90
#, 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 ""
@@ -2446,40 +2435,40 @@ msgstr ""
msgid "Who needs help?"
msgstr "Кому нужна помощь?"
-#: lib/who_need_help_web/live/category_proposal_live.ex:121
+#: lib/who_need_help_web/live/category_proposal_live.ex:127
#, elixir-autogen, elixir-format
msgid "Why is this useful?"
msgstr "Почему это полезно?"
-#: lib/who_need_help_web/live/request_live/show.ex:957
+#: lib/who_need_help_web/live/request_live/show.ex:970
#, elixir-autogen, elixir-format
msgid "Withdraw from this request"
msgstr "Отказаться от этой заявки"
-#: lib/who_need_help_web/live/request_live/show.ex:802
+#: lib/who_need_help_web/live/request_live/show.ex:807
#, elixir-autogen, elixir-format
msgid "Write a safe coordination message…"
msgstr "Напишите безопасное сообщение для координации…"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:209
+#: lib/who_need_help_web/live/activity_live/show.ex:298
+#: lib/who_need_help_web/live/request_live/new.ex:257
+#: lib/who_need_help_web/live/request_live/show.ex:566
#, elixir-autogen, elixir-format
msgid "Yes"
msgstr "Да"
-#: lib/who_need_help_web/live/activity_live/show.ex:309
+#: lib/who_need_help_web/live/activity_live/show.ex:313
#, 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:58
+#: lib/who_need_help_web/live/category_proposal_live.ex:59
#, elixir-autogen, elixir-format
msgid "You already voted for this proposal."
msgstr "Вы уже проголосовали за это предложение."
-#: lib/who_need_help_web/live/activity_live/show.ex:506
+#: lib/who_need_help_web/live/activity_live/show.ex:510
#, elixir-autogen, elixir-format
msgid "You are approved, so the map can show the exact meeting coordinates."
msgstr ""
@@ -2490,8 +2479,8 @@ msgstr ""
msgid "You are matched. Use chat to coordinate safely."
msgstr "Помощник назначен. Безопасно согласуйте детали в чате."
-#: lib/who_need_help_web/live/activity_live/show.ex:317
-#: lib/who_need_help_web/live/request_live/show.ex:605
+#: lib/who_need_help_web/live/activity_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:610
#, elixir-autogen, elixir-format
msgid "You are not allowed to do that."
msgstr "У вас нет разрешения на это действие."
@@ -2501,7 +2490,7 @@ msgstr "У вас нет разрешения на это действие."
msgid "You are running the local mail adapter."
msgstr "Вы используете локальный почтовый адаптер."
-#: lib/who_need_help_web/live/request_live/new.ex:332
+#: lib/who_need_help_web/live/request_live/new.ex:337
#, elixir-autogen, elixir-format
msgid "You can also enter coordinates manually for local testing."
msgstr ""
@@ -2512,12 +2501,12 @@ msgstr ""
msgid "You can be the first person to ask the community."
msgstr "Вы можете первыми обратиться за помощью к сообществу."
-#: lib/who_need_help_web/live/request_live/show.ex:602
+#: lib/who_need_help_web/live/request_live/show.ex:607
#, elixir-autogen, elixir-format
msgid "You cannot accept your own request."
msgstr "Нельзя принять собственную заявку."
-#: lib/who_need_help_web/live/activity_live/show.ex:316
+#: lib/who_need_help_web/live/activity_live/show.ex:320
#, elixir-autogen, elixir-format
msgid "You cannot report your own content."
msgstr "Нельзя жаловаться на собственный контент."
@@ -2573,7 +2562,7 @@ msgstr "Ваше подтверждение сохранено."
msgid "Your profile"
msgstr "Ваш профиль"
-#: lib/who_need_help_web/live/request_live/new.ex:47
+#: lib/who_need_help_web/live/request_live/new.ex:52
#, elixir-autogen, elixir-format
msgid "Your request is now visible to nearby helpers."
msgstr "Теперь вашу заявку видят помощники рядом."
@@ -2583,7 +2572,7 @@ msgstr "Теперь вашу заявку видят помощники ряд
msgid "Your requests"
msgstr "Ваши запросы"
-#: lib/who_need_help_web/live/request_live/show.ex:817
+#: lib/who_need_help_web/live/request_live/show.ex:822
#, elixir-autogen, elixir-format
msgid "Your review is revealed only after both participants submit."
msgstr ""
@@ -2604,13 +2593,13 @@ msgstr "активность"
msgid "activity message"
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:79
#, elixir-autogen, elixir-format
msgid "before confirming."
msgstr "перед подтверждением."
#: 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/category_proposal_live.ex:153
#: 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
@@ -2619,7 +2608,7 @@ msgstr "автор: %{name}"
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:896
#, elixir-autogen, elixir-format
msgid "completed"
msgstr "завершено"
@@ -2634,59 +2623,54 @@ msgstr "истекает %{time}"
msgid "for an account now."
msgstr "учётную запись сейчас."
-#: lib/who_need_help_web/live/request_live/show.ex:910
+#: lib/who_need_help_web/live/request_live/show.ex:923
#, elixir-autogen, elixir-format
msgid "helper movement supported"
msgstr "движение помощника подтверждено"
-#: lib/who_need_help_web/live/request_live/show.ex:911
+#: lib/who_need_help_web/live/request_live/show.ex:924
#, elixir-autogen, elixir-format
msgid "no movement evidence"
msgstr "нет подтверждения движения"
-#: lib/who_need_help_web/live/request_live/show.ex:902
+#: lib/who_need_help_web/live/request_live/show.ex:915
#, elixir-autogen, elixir-format
msgid "no proximity evidence"
msgstr "нет подтверждения близости"
-#: 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:605
+#: lib/who_need_help_web/live/activity_live/show.ex:612
#, elixir-autogen, elixir-format
msgid "organizer"
msgstr "организатор"
-#: lib/who_need_help_web/live/request_live/show.ex:756
+#: lib/who_need_help_web/live/request_live/show.ex:761
#, elixir-autogen, elixir-format
msgid "participants only"
msgstr "только участники"
#: lib/who_need_help_web/live/request_live/index.ex:328
-#: lib/who_need_help_web/live/request_live/show.ex:887
+#: lib/who_need_help_web/live/request_live/show.ex:900
#, elixir-autogen, elixir-format
msgid "people"
msgstr "люди"
-#: lib/who_need_help_web/live/request_live/show.ex:901
+#: lib/who_need_help_web/live/request_live/show.ex:914
#, elixir-autogen, elixir-format
msgid "proximity supported"
msgstr "близость подтверждена"
#: lib/who_need_help_web/live/profile_live.ex:204
-#: lib/who_need_help_web/live/request_live/show.ex:891
+#: lib/who_need_help_web/live/request_live/show.ex:904
#, elixir-autogen, elixir-format
msgid "rating"
msgstr "оценка"
-#: lib/who_need_help_web/live/request_live/show.ex:748
+#: lib/who_need_help_web/live/request_live/show.ex:753
#, elixir-autogen, elixir-format
msgid "rating %{rating}"
msgstr "оценка %{rating}"
-#: 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:78
#, elixir-autogen, elixir-format
msgid "safety rules"
msgstr "Правила безопасности"
@@ -2706,12 +2690,12 @@ msgstr "в свою учётную запись сейчас."
msgid "unique people"
msgstr "Уникальные люди"
-#: 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/activity_live/show.ex:418
+#: lib/who_need_help_web/live/activity_live/show.ex:572
+#: lib/who_need_help_web/live/activity_live/show.ex:631
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:945
#, elixir-autogen, elixir-format
msgid "unverified"
msgstr "не подтверждено"
@@ -2721,13 +2705,13 @@ msgstr "не подтверждено"
msgid "urgent"
msgstr "срочно"
-#: 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/activity_live/show.ex:417
+#: lib/who_need_help_web/live/activity_live/show.ex:571
+#: lib/who_need_help_web/live/activity_live/show.ex:630
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:739
+#: lib/who_need_help_web/live/request_live/show.ex:944
#, elixir-autogen, elixir-format
msgid "verified"
msgstr "подтверждено"
@@ -2737,18 +2721,18 @@ msgstr "подтверждено"
msgid "verified handovers"
msgstr "подтверждённые передачи"
-#: lib/who_need_help_web/live/request_live/show.ex:660
+#: lib/who_need_help_web/live/request_live/show.ex:665
#, elixir-autogen, elixir-format
msgid "← All requests"
msgstr "← Все заявки"
-#: lib/who_need_help_web/live/activity_live/new.ex:113
-#: lib/who_need_help_web/live/activity_live/show.ex:348
+#: lib/who_need_help_web/live/activity_live/new.ex:118
+#: lib/who_need_help_web/live/activity_live/show.ex:352
#, elixir-autogen, elixir-format
msgid "← Back to activities"
msgstr "← Назад к активностям"
-#: lib/who_need_help_web/live/request_live/new.ex:152
+#: lib/who_need_help_web/live/request_live/new.ex:157
#, elixir-autogen, elixir-format
msgid "← Back to requests"
msgstr "← Назад к заявкам"
@@ -2809,15 +2793,15 @@ msgstr "Повторяющаяся пара участников"
msgid "Unusual action rate"
msgstr "Необычная частота действий"
-#: lib/who_need_help_web/live/activity_live/show.ex:448
-#: lib/who_need_help_web/live/request_live/show.ex:768
+#: lib/who_need_help_web/live/activity_live/show.ex:452
+#: lib/who_need_help_web/live/request_live/show.ex:773
#, elixir-autogen, elixir-format
msgid "Load earlier messages"
msgstr "Загрузить предыдущие сообщения"
#: 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/category_proposal_live.ex:168
#: lib/who_need_help_web/live/leaderboard_live.ex:87
#: lib/who_need_help_web/live/moderation_live.ex:384
#: lib/who_need_help_web/live/moderation_live.ex:419
@@ -2832,9 +2816,9 @@ msgid "Load more"
msgstr "Загрузить ещё"
#: 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/activity_live/show.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:314
-#: lib/who_need_help_web/live/request_live/show.ex:849
+#: lib/who_need_help_web/live/request_live/show.ex:862
#, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable."
msgstr "Карта недоступна в этом браузере. Остальная информация по-прежнему доступна."
@@ -2855,19 +2839,19 @@ msgstr "Подтвердите новый email для своей учётной
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
+#: lib/who_need_help_web/live/activity_live/show.ex:340
+#: lib/who_need_help_web/live/category_proposal_live.ex:61
+#: lib/who_need_help_web/live/request_live/show.ex:626
#, 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
+#: lib/who_need_help_web/live/activity_live/new.ex:110
#, 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
+#: lib/who_need_help_web/live/request_live/new.ex:149
#, elixir-autogen, elixir-format
msgid "Could not publish the request. Please try again."
msgstr "Не удалось опубликовать заявку. Попробуйте ещё раз."
@@ -2882,32 +2866,32 @@ msgstr "Не удалось разблокировать пользовател
msgid "JavaScript is required to confirm this protected email link."
msgstr "Для подтверждения этой защищённой ссылки на email необходим JavaScript."
-#: lib/who_need_help_web/live/request_live/show.ex:594
+#: lib/who_need_help_web/live/request_live/show.ex:599
#, elixir-autogen, elixir-format
msgid "Last update: %{time}"
msgstr "Последнее обновление: %{time}"
-#: lib/who_need_help_web/live/request_live/show.ex:617
+#: lib/who_need_help_web/live/request_live/show.ex:622
#, elixir-autogen, elixir-format
msgid "Live location sharing is not active."
msgstr "Передача геопозиции сейчас не активна."
-#: lib/who_need_help_web/live/request_live/show.ex:592
+#: lib/who_need_help_web/live/request_live/show.ex:597
#, elixir-autogen, elixir-format
msgid "Shared location"
msgstr "Передаваемая геопозиция"
-#: lib/who_need_help_web/live/activity_live/show.ex:329
+#: lib/who_need_help_web/live/activity_live/show.ex:333
#, elixir-autogen, elixir-format
msgid "The organizer cannot leave the activity."
msgstr "Организатор не может покинуть активность."
-#: lib/who_need_help_web/live/activity_live/show.ex:326
+#: lib/who_need_help_web/live/activity_live/show.ex:330
#, elixir-autogen, elixir-format
msgid "The organizer is already attending."
msgstr "Организатор уже участвует."
-#: lib/who_need_help_web/controllers/user_registration_controller.ex:60
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:58
#, elixir-autogen, elixir-format
msgid "The registration form is invalid."
msgstr "Форма регистрации заполнена неверно."
@@ -2927,22 +2911,22 @@ msgstr "Форма настроек заполнена неверно."
msgid "The sign-in form is invalid."
msgstr "Форма входа заполнена неверно."
-#: lib/who_need_help_web/live/activity_live/show.ex:323
+#: lib/who_need_help_web/live/activity_live/show.ex:327
#, elixir-autogen, elixir-format
msgid "This activity is no longer available."
msgstr "Эта активность больше недоступна."
-#: lib/who_need_help_web/live/request_live/show.ex:616
+#: lib/who_need_help_web/live/request_live/show.ex:621
#, elixir-autogen, elixir-format
msgid "This match is no longer active."
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 "This proposal is no longer available."
msgstr "Это предложение больше недоступно."
-#: lib/who_need_help_web/live/request_live/show.ex:614
+#: lib/who_need_help_web/live/request_live/show.ex:619
#, elixir-autogen, elixir-format
msgid "This request has expired."
msgstr "Срок действия этой заявки истёк."
@@ -2957,12 +2941,12 @@ msgstr "Слишком много попыток входа за настрое
msgid "Your secure email link is ready. Continue to sign in."
msgstr "Защищённая ссылка из письма готова. Продолжите вход."
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:128
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:130
#, 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
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:107
#, elixir-autogen, elixir-format
msgid "If this address can be registered or signed in, login instructions will arrive shortly."
msgstr "Если этот адрес можно зарегистрировать или использовать для входа, инструкции для входа скоро придут."
@@ -2970,9 +2954,191 @@ msgstr "Если этот адрес можно зарегистрировать
#: lib/who_need_help_web/live/profile_live.ex:62
#, elixir-autogen, elixir-format
msgid "This account cannot perform that action."
-msgstr ""
+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 за настроенный промежуток времени."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:32
+#, elixir-autogen, elixir-format
+msgid "A Google account is connected. You can use it to sign in."
+msgstr "Аккаунт Google подключён. Теперь с его помощью можно входить."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:207
+#, elixir-autogen, elixir-format
+msgid "A different Google account is already connected."
+msgstr "Уже подключён другой аккаунт Google."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:17
+#, elixir-autogen, elixir-format
+msgid "A password is not required. We will email a one-time confirmation link. You can add a password later in account settings."
+msgstr "Пароль не обязателен. Мы отправим на email одноразовую ссылку для подтверждения. Пароль можно добавить позже в настройках аккаунта."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:170
+#, elixir-autogen, elixir-format
+msgid "An account with this email already exists. Sign in by email or password, then connect Google in account settings."
+msgstr "Учётная запись с таким email уже существует. Войдите по email или паролю, затем подключите Google в настройках аккаунта."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:37
+#, elixir-autogen, elixir-format
+msgid "Confirm that you are 18 or older and accept the safety rules first."
+msgstr "Сначала подтвердите, что вам уже исполнилось 18 лет, и примите правила безопасности."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:49
+#, elixir-autogen, elixir-format
+msgid "Connect Google account"
+msgstr "Подключить аккаунт Google"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#, elixir-autogen, elixir-format
+msgid "Connect Google only after signing in here. We never merge accounts automatically by matching email addresses."
+msgstr "Подключайте Google только после входа здесь. Мы никогда не объединяем аккаунты автоматически по совпадению email."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "Create account and email me a link"
+msgstr "Создать аккаунт и отправить ссылку на email"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:99
+#, elixir-autogen, elixir-format
+msgid "Email me a sign-in link"
+msgstr "Отправить ссылку для входа на email"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:215
+#, elixir-autogen, elixir-format
+msgid "Google account connection session is invalid."
+msgstr "Сессия подключения аккаунта Google недействительна."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:97
+#, elixir-autogen, elixir-format
+msgid "Google did not provide a verified email address."
+msgstr "Google не предоставил подтверждённый адрес email."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:29
+#, elixir-autogen, elixir-format
+msgid "Google sign-in"
+msgstr "Вход через Google"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:197
+#, elixir-autogen, elixir-format
+msgid "Google sign-in connected."
+msgstr "Вход через Google подключён."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:123
+#, elixir-autogen, elixir-format
+msgid "Google sign-in could not be started."
+msgstr "Не удалось начать вход через Google."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:102
+#, elixir-autogen, elixir-format
+msgid "Google sign-in failed. Please try again."
+msgstr "Не удалось войти через Google. Попробуйте ещё раз."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:128
+#, elixir-autogen, elixir-format
+msgid "Google sign-in is not configured yet."
+msgstr "Вход через Google пока не настроен."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:87
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session expired. Please start again."
+msgstr "Сессия входа через Google истекла. Начните заново."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:91
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session is invalid."
+msgstr "Сессия входа через Google недействительна."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:47
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:53
+#, elixir-autogen, elixir-format
+msgid "Google sign-in will be available after the operator configures it."
+msgstr "Вход через Google станет доступен после настройки оператором."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:95
+#, elixir-autogen, elixir-format
+msgid "Sending link..."
+msgstr "Отправляем ссылку..."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:74
+#, elixir-autogen, elixir-format
+msgid "Sign in with Google"
+msgstr "Войти через Google"
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43
+#, elixir-autogen, elixir-format
+msgid "Sign up with Google"
+msgstr "Зарегистрироваться через Google"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:202
+#, elixir-autogen, elixir-format
+msgid "That Google account belongs to another local account."
+msgstr "Этот аккаунт Google принадлежит другой локальной учётной записи."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:45
+#, elixir-autogen, elixir-format
+msgid "The Google registration form is invalid."
+msgstr "Форма регистрации через Google заполнена неверно."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:260
+#, elixir-autogen, elixir-format
+msgid "This Google account cannot be used right now."
+msgstr "Этот аккаунт Google сейчас нельзя использовать."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:144
+#, elixir-autogen, elixir-format
+msgid "This Google account is not connected yet. Create an account or sign in by email and connect Google in account settings."
+msgstr "Этот аккаунт Google ещё не подключён. Создайте учётную запись или войдите по email и подключите Google в настройках аккаунта."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:109
+#, elixir-autogen, elixir-format
+msgid "This works only if you previously added a password in account settings."
+msgstr "Это работает, только если вы ранее добавили пароль в настройках аккаунта."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#, elixir-autogen, elixir-format
+msgid "Use a password instead"
+msgstr "Войти по паролю"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "We will send a one-time sign-in link. No password is required."
+msgstr "Мы отправим одноразовую ссылку для входа. Пароль не требуется."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:163
+#, elixir-autogen, elixir-format
+msgid "Your account was created with Google."
+msgstr "Ваша учётная запись создана через Google."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:51
+#, elixir-autogen, elixir-format
+msgid "or sign up with email"
+msgstr "или зарегистрироваться по email"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82
+#, elixir-autogen, elixir-format
+msgid "or use email"
+msgstr "или использовать email"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:70
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google"
+msgstr "Отключить Google"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:68
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google sign-in from this account?"
+msgstr "Отключить вход через Google для этой учётной записи?"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:60
+#, elixir-autogen, elixir-format
+msgid "Google sign-in disconnected."
+msgstr "Вход через Google отключён."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:65
+#, elixir-autogen, elixir-format
+msgid "No Google account is connected."
+msgstr "Аккаунт Google не подключён."
diff --git a/priv/gettext/uk/LC_MESSAGES/default.po b/priv/gettext/uk/LC_MESSAGES/default.po
index 4a4d4e6..6609371 100644
--- a/priv/gettext/uk/LC_MESSAGES/default.po
+++ b/priv/gettext/uk/LC_MESSAGES/default.po
@@ -17,7 +17,7 @@ msgstr ""
msgid "Account settings"
msgstr "Параметри облікового запису"
-#: lib/who_need_help_web/components/core_components.ex:393
+#: lib/who_need_help_web/components/core_components.ex:446
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr "Дії"
@@ -25,7 +25,7 @@ msgstr "Дії"
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:23
#, elixir-autogen, elixir-format
msgid "Ask for help"
msgstr "Попросити допомоги"
@@ -108,7 +108,7 @@ msgstr "Немає з’єднання з інтернетом"
msgid "close"
msgstr "закрити"
-#: lib/who_need_help_web/live/activity_live/show.ex:363
+#: lib/who_need_help_web/live/activity_live/show.ex:367
#, elixir-autogen, elixir-format
msgid "%{approved}/%{capacity} approved"
msgstr "Схвалено: %{approved}/%{capacity}"
@@ -118,37 +118,37 @@ msgstr "Схвалено: %{approved}/%{capacity}"
msgid "%{count} community votes"
msgstr "Голосів спільноти: %{count}"
-#: lib/who_need_help_web/live/request_live/show.ex:745
+#: lib/who_need_help_web/live/request_live/show.ex:750
#, elixir-autogen, elixir-format
msgid "%{count} unique people"
msgstr "%{count} унікальних людей"
-#: lib/who_need_help_web/live/request_live/show.ex:829
+#: lib/who_need_help_web/live/request_live/show.ex:842
#, elixir-autogen, elixir-format
msgid "1 — unsafe"
msgstr "1 — небезпечно"
-#: lib/who_need_help_web/live/request_live/show.ex:828
+#: lib/who_need_help_web/live/request_live/show.ex:841
#, elixir-autogen, elixir-format
msgid "2 — poor"
msgstr "2 — погано"
-#: lib/who_need_help_web/live/request_live/show.ex:827
+#: lib/who_need_help_web/live/request_live/show.ex:840
#, elixir-autogen, elixir-format
msgid "3 — okay"
msgstr "3 — нормально"
-#: lib/who_need_help_web/live/request_live/show.ex:826
+#: lib/who_need_help_web/live/request_live/show.ex:839
#, elixir-autogen, elixir-format
msgid "4 — good"
msgstr "4 — добре"
-#: lib/who_need_help_web/live/request_live/show.ex:825
+#: lib/who_need_help_web/live/request_live/show.ex:838
#, elixir-autogen, elixir-format
msgid "5 — excellent"
msgstr "5 — відмінно"
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:118
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:120
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
@@ -162,13 +162,13 @@ msgstr ""
"Сигнал потребує перевірки людиною; відсутність необов’язкового відстеження "
"не є доказом зловживання."
-#: lib/who_need_help_web/live/request_live/show.ex:859
+#: lib/who_need_help_web/live/request_live/show.ex:872
#, 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:633
+#: lib/who_need_help_web/live/request_live/show.ex:638
#, elixir-autogen, elixir-format
msgid "Accepted"
msgstr "Прийнято"
@@ -189,7 +189,7 @@ msgstr "Акаунт і мова"
msgid "Accounts"
msgstr "Акаунти"
-#: lib/who_need_help_web/live/request_live/show.ex:855
+#: lib/who_need_help_web/live/request_live/show.ex:868
#, elixir-autogen, elixir-format
msgid "Action center"
msgstr "Центр дій"
@@ -217,7 +217,7 @@ msgstr ""
"узгодьте маршрут і заходи безпеки та ніколи не публікуйте домашні адреси, "
"коди доступу чи документи для подорожі."
-#: lib/who_need_help_web/live/activity_live/new.ex:163
+#: lib/who_need_help_web/live/activity_live/new.ex:168
#, elixir-autogen, elixir-format
msgid "Activity details"
msgstr "Деталі активності"
@@ -228,12 +228,12 @@ msgid "Activity hidden."
msgstr "Активність приховано."
#: lib/who_need_help_web/live/activity_live/show.ex:18
-#: lib/who_need_help_web/live/activity_live/show.ex:177
+#: lib/who_need_help_web/live/activity_live/show.ex:181
#, elixir-autogen, elixir-format
msgid "Activity is unavailable."
msgstr "Активність недоступна."
-#: lib/who_need_help_web/live/activity_live/new.ex:36
+#: lib/who_need_help_web/live/activity_live/new.ex:41
#, elixir-autogen, elixir-format
msgid "Activity published. You approve every participant."
msgstr "Активність опубліковано. Ви схвалюєте кожного учасника."
@@ -244,7 +244,7 @@ msgid "Activity restored."
msgstr "Активність відновлено."
#: lib/who_need_help_web/live/activity_live/index.ex:210
-#: lib/who_need_help_web/live/activity_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Activity type"
msgstr "Тип активності"
@@ -275,7 +275,7 @@ msgstr "Усі категорії"
msgid "Already registered?"
msgstr "Уже зареєстровані?"
-#: lib/who_need_help_web/live/request_live/show.ex:603
+#: lib/who_need_help_web/live/request_live/show.ex:608
#, elixir-autogen, elixir-format
msgid "Another helper already accepted this request."
msgstr "Інший помічник уже прийняв цю заявку."
@@ -285,60 +285,60 @@ msgstr "Інший помічник уже прийняв цю заявку."
msgid "Any urgency"
msgstr "Будь-яка терміновість"
-#: lib/who_need_help_web/live/activity_live/show.ex:524
+#: lib/who_need_help_web/live/activity_live/show.ex:528
#, elixir-autogen, elixir-format
msgid "Approval pending"
msgstr "Очікує схвалення"
-#: lib/who_need_help_web/live/activity_live/show.ex:574
+#: lib/who_need_help_web/live/activity_live/show.ex:581
#, elixir-autogen, elixir-format
msgid "Approve"
msgstr "Схвалити"
-#: lib/who_need_help_web/live/activity_live/show.ex:435
+#: lib/who_need_help_web/live/activity_live/show.ex:439
#, elixir-autogen, elixir-format
msgid "Approved group chat"
msgstr "Чат схваленої групи"
-#: lib/who_need_help_web/live/activity_live/show.ex:600
+#: lib/who_need_help_web/live/activity_live/show.ex:607
#, 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:258
-#: lib/who_need_help_web/live/request_live/new.ex:306
+#: lib/who_need_help_web/live/activity_live/new.ex:263
+#: lib/who_need_help_web/live/request_live/new.ex:311
#, elixir-autogen, elixir-format
msgid "Approximate area"
msgstr "Орієнтовний район"
-#: lib/who_need_help_web/live/activity_live/new.ex:250
+#: lib/who_need_help_web/live/activity_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Approximate public meeting area"
msgstr "Орієнтовний публічний район зустрічі"
#: lib/who_need_help_web/live/profile_live.ex:167
-#: lib/who_need_help_web/live/request_live/show.ex:639
+#: lib/who_need_help_web/live/request_live/show.ex:644
#, elixir-autogen, elixir-format
msgid "Approximate publicly"
msgstr "Публічно — приблизно"
-#: lib/who_need_help_web/live/request_live/new.ex:314
+#: lib/who_need_help_web/live/request_live/new.ex:319
#, elixir-autogen, elixir-format
msgid "Approximate publicly (recommended)"
msgstr "Показувати приблизно (рекомендовано)"
-#: lib/who_need_help_web/live/request_live/show.ex:702
+#: lib/who_need_help_web/live/request_live/show.ex:707
#, elixir-autogen, elixir-format
msgid "Area:"
msgstr "Район:"
-#: lib/who_need_help_web/live/request_live/new.ex:154
+#: lib/who_need_help_web/live/request_live/new.ex:159
#, elixir-autogen, elixir-format
msgid "Ask for urgent help"
msgstr "Попросити термінову допомогу"
-#: lib/who_need_help_web/live/category_proposal_live.ex:92
+#: lib/who_need_help_web/live/category_proposal_live.ex:98
#, elixir-autogen, elixir-format
msgid "Bicycle puncture"
msgstr "Прокол колеса велосипеда"
@@ -358,12 +358,12 @@ msgstr ""
msgid "Block and report"
msgstr "Блокування та скарга"
-#: lib/who_need_help_web/live/activity_live/show.ex:641
+#: lib/who_need_help_web/live/activity_live/show.ex:648
#, elixir-autogen, elixir-format
msgid "Block organizer"
msgstr "Заблокувати організатора"
-#: lib/who_need_help_web/live/request_live/show.ex:1062
+#: lib/who_need_help_web/live/request_live/show.ex:1075
#, elixir-autogen, elixir-format
msgid "Block this user"
msgstr "Заблокувати користувача"
@@ -380,7 +380,7 @@ msgstr ""
"Обидва учасники підтверджують завершення, а помічник вводить одноразовий "
"код."
-#: lib/who_need_help_web/live/request_live/new.ex:118
+#: lib/who_need_help_web/live/request_live/new.ex:123
#, elixir-autogen, elixir-format
msgid "Briefly describe the help you need"
msgstr "Коротко опишіть, яка допомога вам потрібна"
@@ -400,33 +400,33 @@ msgstr "Звіт про розробку"
msgid "COMMUNITY TRUST"
msgstr "ДОВІРА СПІЛЬНОТИ"
-#: lib/who_need_help_web/live/category_proposal_live.ex:80
+#: lib/who_need_help_web/live/category_proposal_live.ex:81
#, elixir-autogen, elixir-format
msgid "COMMUNITY-DRIVEN TAXONOMY"
msgstr "КАТЕГОРІЇ ВІД СПІЛЬНОТИ"
-#: lib/who_need_help_web/live/activity_live/show.ex:594
+#: lib/who_need_help_web/live/activity_live/show.ex:601
#, elixir-autogen, elixir-format
msgid "Cancel activity"
msgstr "Скасувати активність"
-#: lib/who_need_help_web/live/request_live/show.ex:873
-#: lib/who_need_help_web/live/request_live/show.ex:967
+#: lib/who_need_help_web/live/request_live/show.ex:886
+#: lib/who_need_help_web/live/request_live/show.ex:980
#, elixir-autogen, elixir-format
msgid "Cancel request"
msgstr "Скасувати запит"
#: 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/activity_live/show.ex:344
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:641
#, elixir-autogen, elixir-format
msgid "Cancelled"
msgstr "Скасовано"
#: lib/who_need_help_web/live/request_live/index.ex:225
-#: lib/who_need_help_web/live/request_live/new.ex:180
+#: lib/who_need_help_web/live/request_live/new.ex:185
#, elixir-autogen, elixir-format
msgid "Category"
msgstr "Категорія"
@@ -436,13 +436,13 @@ msgstr "Категорія"
msgid "Category created and proposal approved."
msgstr "Категорію створено, а пропозицію схвалено."
-#: lib/who_need_help_web/live/request_live/new.ex:211
-#: lib/who_need_help_web/live/request_live/show.ex:691
+#: lib/who_need_help_web/live/request_live/new.ex:216
+#: lib/who_need_help_web/live/request_live/show.ex:696
#, 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/category_proposal_live.ex:67
#: lib/who_need_help_web/live/moderation_live.ex:466
#, elixir-autogen, elixir-format
msgid "Category proposals"
@@ -459,7 +459,7 @@ msgid "Change role"
msgstr "Змінити роль"
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:21
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:47
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:107
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr "Зміна…"
@@ -470,31 +470,31 @@ msgid "Choose a category, urgency, and safe location visibility."
msgstr ""
"Оберіть категорію, терміновість і безпечний рівень видимості геопозиції."
-#: lib/who_need_help_web/live/activity_live/new.ex:140
+#: lib/who_need_help_web/live/activity_live/new.ex:145
#, elixir-autogen, elixir-format
msgid "Choose activity"
msgstr "Оберіть активність"
-#: lib/who_need_help_web/live/request_live/new.ex:181
+#: lib/who_need_help_web/live/request_live/new.ex:186
#, elixir-autogen, elixir-format
msgid "Choose category"
msgstr "Виберіть категорію"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:180
+#: lib/who_need_help_web/live/activity_live/new.ex:207
+#: lib/who_need_help_web/live/request_live/new.ex:228
+#: lib/who_need_help_web/live/request_live/new.ex:255
#, elixir-autogen, elixir-format
msgid "Choose…"
msgstr "Оберіть…"
-#: lib/who_need_help_web/live/activity_live/new.ex:251
+#: lib/who_need_help_web/live/activity_live/new.ex:256
#, elixir-autogen, elixir-format
msgid "City centre, near the main entrance"
msgstr "Центр міста, біля головного входу"
-#: lib/who_need_help_web/live/activity_live/show.ex:654
-#: lib/who_need_help_web/live/request_live/show.ex:1078
+#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/request_live/show.ex:1096
#, elixir-autogen, elixir-format
msgid "Clear"
msgstr "Очистити"
@@ -504,7 +504,7 @@ msgstr "Очистити"
msgid "Close"
msgstr "Закрити"
-#: lib/who_need_help_web/live/activity_live/new.ex:220
+#: lib/who_need_help_web/live/activity_live/new.ex:225
#, elixir-autogen, elixir-format
msgid "Coffee and conversation"
msgstr "Кава й розмова"
@@ -516,7 +516,7 @@ msgstr ""
"Кава, кіно, прогулянки та походи відокремлені від термінової допомоги й "
"ніколи не впливають на рейтинг помічника."
-#: lib/who_need_help_web/live/request_live/show.ex:835
+#: lib/who_need_help_web/live/request_live/show.ex:848
#, elixir-autogen, elixir-format
msgid "Comment (optional)"
msgstr "Коментар (необов’язково)"
@@ -527,19 +527,19 @@ msgid "Community helpers"
msgstr "Помічники спільноти"
#: 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/activity_live/show.ex:396
+#: lib/who_need_help_web/live/activity_live/show.ex:553
+#: lib/who_need_help_web/live/activity_live/show.ex:610
#: 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:167
-#: lib/who_need_help_web/live/activity_live/show.ex:339
+#: lib/who_need_help_web/live/activity_live/show.ex:343
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:640
#, elixir-autogen, elixir-format
msgid "Completed"
msgstr "Завершено"
@@ -554,25 +554,25 @@ msgstr "Підтвердити й увійти лише зараз"
msgid "Confirm and stay logged in"
msgstr "Підтвердити й залишатися в системі"
-#: lib/who_need_help_web/live/request_live/show.ex:947
+#: lib/who_need_help_web/live/request_live/show.ex:960
#, elixir-autogen, elixir-format
msgid "Confirm my part is complete"
msgstr "Підтвердити виконання своєї частини"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:42
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:102
#, elixir-autogen, elixir-format
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:611
+#: lib/who_need_help_web/live/category_proposal_live.ex:52
+#: lib/who_need_help_web/live/request_live/show.ex:616
#, 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:100
-#: lib/who_need_help_web/live/request_live/new.ex:139
+#: lib/who_need_help_web/live/activity_live/new.ex:105
+#: lib/who_need_help_web/live/request_live/new.ex:144
#, elixir-autogen, elixir-format
msgid "Confirm your email and ensure your account is active before publishing."
msgstr ""
@@ -597,17 +597,12 @@ msgid "Create a clear plan in a public place."
msgstr "Створіть зрозумілий план у громадському місці."
#: lib/who_need_help_web/live/activity_live/index.ex:188
-#: lib/who_need_help_web/live/activity_live/new.ex:11
+#: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format
msgid "Create activity"
msgstr "Створити активність"
-#: 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:115
+#: lib/who_need_help_web/live/activity_live/new.ex:120
#, elixir-autogen, elixir-format
msgid "Create an activity"
msgstr "Створити активність"
@@ -622,18 +617,13 @@ msgstr "Створити категорію"
msgid "Create urgent request"
msgstr "Створити термінову заявку"
-#: 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:663
+#: lib/who_need_help_web/live/activity_live/show.ex:675
#, elixir-autogen, elixir-format
msgid "Dangerous plan"
msgstr "Небезпечний план"
#: lib/who_need_help_web/live/moderation_live.ex:248
-#: lib/who_need_help_web/live/request_live/show.ex:1086
+#: lib/who_need_help_web/live/request_live/show.ex:1104
#, elixir-autogen, elixir-format
msgid "Dangerous request"
msgstr "Небезпечна заявка"
@@ -655,7 +645,7 @@ msgstr ""
"Рішення та доступ до матеріалів зі скарг на листування записуються до "
"журналу аудиту."
-#: lib/who_need_help_web/live/activity_live/show.ex:581
+#: lib/who_need_help_web/live/activity_live/show.ex:588
#, elixir-autogen, elixir-format
msgid "Decline"
msgstr "Відхилити"
@@ -665,7 +655,7 @@ msgstr "Відхилити"
msgid "Default location visibility"
msgstr "Типова видимість геопозиції"
-#: lib/who_need_help_web/live/activity_live/new.ex:227
+#: lib/who_need_help_web/live/activity_live/new.ex:232
#, elixir-autogen, elixir-format
msgid "Describe the public plan, approximate duration, and who might enjoy it."
msgstr ""
@@ -678,7 +668,7 @@ msgstr ""
msgid "Dismissed"
msgstr "Відхилено"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:34
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:71
#: lib/who_need_help_web/live/profile_live.ex:149
#, elixir-autogen, elixir-format
msgid "Display name"
@@ -697,14 +687,14 @@ msgstr ""
msgid "Don't have an account?"
msgstr "Немає акаунта?"
-#: lib/who_need_help_web/live/request_live/show.ex:815
+#: lib/who_need_help_web/live/request_live/show.ex:820
#, 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:71
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:62
#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:89
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:122
#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:15
#, elixir-autogen, elixir-format
msgid "Email"
@@ -745,7 +735,7 @@ msgstr "Опис англійською"
msgid "English name"
msgstr "Назва англійською"
-#: lib/who_need_help_web/live/request_live/show.ex:992
+#: lib/who_need_help_web/live/request_live/show.ex:1005
#, elixir-autogen, elixir-format
msgid "Enter handover code"
msgstr "Введіть код передачі"
@@ -755,7 +745,7 @@ msgstr "Введіть код передачі"
msgid "Exact address after matching"
msgstr "Точна адреса після призначення помічника"
-#: lib/who_need_help_web/live/activity_live/new.ex:274
+#: lib/who_need_help_web/live/activity_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Exact coordinates are shown only to participants you approve."
msgstr "Точні координати показуються лише схваленим вами учасникам."
@@ -765,29 +755,29 @@ msgstr "Точні координати показуються лише схва
msgid "Exact for active match"
msgstr "Точно — для активної заявки"
-#: lib/who_need_help_web/live/request_live/new.ex:316
-#: lib/who_need_help_web/live/request_live/show.ex:641
+#: lib/who_need_help_web/live/request_live/new.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:646
#, elixir-autogen, elixir-format
msgid "Exact only for active match"
msgstr "Точно лише для активної заявки"
#: lib/who_need_help_web/live/profile_live.ex:169
-#: lib/who_need_help_web/live/request_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:647
#, elixir-autogen, elixir-format
msgid "Exact publicly"
msgstr "Публічно — точно"
-#: lib/who_need_help_web/live/request_live/new.ex:317
+#: lib/who_need_help_web/live/request_live/new.ex:322
#, elixir-autogen, elixir-format
msgid "Exact publicly — explicit consent"
msgstr "Публічно — точно, з явною згодою"
-#: lib/who_need_help_web/live/request_live/show.ex:710
+#: lib/who_need_help_web/live/request_live/show.ex:715
#, elixir-autogen, elixir-format
msgid "Expires:"
msgstr "Спливає:"
-#: lib/who_need_help_web/live/request_live/new.ex:274
+#: lib/who_need_help_web/live/request_live/new.ex:279
#, elixir-autogen, elixir-format
msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "Поясніть, що вже організовано й що має зробити волонтер."
@@ -797,9 +787,9 @@ msgstr "Поясніть, що вже організовано й що має з
msgid "Find people to join"
msgstr "Знайти компанію"
-#: lib/who_need_help_web/live/activity_live/show.ex:664
+#: lib/who_need_help_web/live/activity_live/show.ex:676
#: lib/who_need_help_web/live/moderation_live.ex:250
-#: lib/who_need_help_web/live/request_live/show.ex:1088
+#: lib/who_need_help_web/live/request_live/show.ex:1106
#, elixir-autogen, elixir-format
msgid "Fraud"
msgstr "Шахрайство"
@@ -821,7 +811,7 @@ msgstr ""
"Підтвердження через GitHub недоступне, доки оператор не налаштує облікові "
"дані OAuth. Посилання, введене вручну, не вважається підтвердженим."
-#: lib/who_need_help_web/live/request_live/show.ex:975
+#: lib/who_need_help_web/live/request_live/show.ex:988
#, elixir-autogen, elixir-format
msgid "HANDOVER CODE FOR THE HELPER"
msgstr "КОД ПЕРЕДАЧІ ДЛЯ ПОМІЧНИКА"
@@ -836,9 +826,9 @@ msgstr "Ім’я користувача (необов’язково)"
msgid "Handover code verified."
msgstr "Код передачі підтверджено."
-#: lib/who_need_help_web/live/activity_live/show.ex:662
+#: lib/who_need_help_web/live/activity_live/show.ex:674
#: lib/who_need_help_web/live/moderation_live.ex:249
-#: lib/who_need_help_web/live/request_live/show.ex:1087
+#: lib/who_need_help_web/live/request_live/show.ex:1105
#, elixir-autogen, elixir-format
msgid "Harassment"
msgstr "Домагання"
@@ -862,7 +852,7 @@ msgstr ""
msgid "Helper"
msgstr "Помічник"
-#: lib/who_need_help_web/live/request_live/show.ex:878
+#: lib/who_need_help_web/live/request_live/show.ex:891
#, elixir-autogen, elixir-format
msgid "Helper: %{name}"
msgstr "Помічник: %{name}"
@@ -913,10 +903,10 @@ msgstr ""
"\n"
"Якщо ви не запитували цей лист, просто проігноруйте його."
-#: lib/who_need_help_web/live/activity_live/new.ex:259
+#: lib/who_need_help_web/live/activity_live/new.ex:264
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:320
+#: lib/who_need_help_web/live/request_live/show.ex:645
#, elixir-autogen, elixir-format
msgid "Hidden"
msgstr "Приховано"
@@ -942,18 +932,19 @@ msgstr "Сховати запит"
msgid "Human decisions retained"
msgstr "Рішення залишаються за людиною"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:48
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:39
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:85
#, 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:862
+#: lib/who_need_help_web/live/request_live/show.ex:875
#, elixir-autogen, elixir-format
msgid "I can help"
msgstr "Я можу допомогти"
-#: lib/who_need_help_web/live/request_live/new.ex:127
+#: lib/who_need_help_web/live/request_live/new.ex:132
#, 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 ""
@@ -961,21 +952,21 @@ msgstr ""
"перебувають поза активним рухом або екстрені служби вже викликано, а "
"запитувана допомога є законною."
-#: lib/who_need_help_web/live/request_live/new.ex:121
+#: lib/who_need_help_web/live/request_live/new.ex:126
#, 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:133
+#: lib/who_need_help_web/live/request_live/new.ex:138
#, 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:298
+#: lib/who_need_help_web/live/activity_live/new.ex:303
#, 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 ""
@@ -994,14 +985,14 @@ msgstr ""
"Якщо ця адреса електронної пошти є в нашій системі, незабаром ви отримаєте "
"інструкції для входу."
-#: lib/who_need_help_web/live/activity_live/show.ex:666
+#: lib/who_need_help_web/live/activity_live/show.ex:678
#: lib/who_need_help_web/live/moderation_live.ex:253
-#: lib/who_need_help_web/live/request_live/show.ex:1091
+#: lib/who_need_help_web/live/request_live/show.ex:1109
#, elixir-autogen, elixir-format
msgid "Impersonation"
msgstr "Видавання себе за іншу особу"
-#: lib/who_need_help_web/live/request_live/show.ex:1027
+#: lib/who_need_help_web/live/request_live/show.ex:1040
#, 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 ""
@@ -1011,7 +1002,7 @@ msgstr ""
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:639
#, elixir-autogen, elixir-format
msgid "In progress"
msgstr "В процесі"
@@ -1031,51 +1022,51 @@ msgstr "Внутрішня нотатка"
msgid "Invalid email or password"
msgstr "Неправильна електронна пошта або пароль"
-#: lib/who_need_help_web/live/activity_live/show.ex:314
+#: lib/who_need_help_web/live/activity_live/show.ex:318
#, elixir-autogen, elixir-format
msgid "Join requests are closed."
msgstr "Подання заявок на участь закрито."
-#: lib/who_need_help_web/live/activity_live/show.ex:380
+#: lib/who_need_help_web/live/activity_live/show.ex:384
#, elixir-autogen, elixir-format
msgid "Join requests close"
msgstr "Приймання заявок завершується"
-#: lib/who_need_help_web/live/activity_live/new.ex:239
+#: lib/who_need_help_web/live/activity_live/new.ex:244
#, elixir-autogen, elixir-format
msgid "Join requests close (UTC)"
msgstr "Завершення прийому заявок (UTC)"
-#: lib/who_need_help_web/live/activity_live/new.ex:282
-#: lib/who_need_help_web/live/request_live/new.ex:340
+#: lib/who_need_help_web/live/activity_live/new.ex:287
+#: lib/who_need_help_web/live/request_live/new.ex:345
#, elixir-autogen, elixir-format
msgid "Latitude"
msgstr "Широта"
-#: lib/who_need_help_web/live/activity_live/show.ex:534
+#: lib/who_need_help_web/live/activity_live/show.ex:541
#, elixir-autogen, elixir-format
msgid "Leave activity"
msgstr "Залишити активність"
-#: lib/who_need_help_web/live/request_live/show.ex:1025
+#: lib/who_need_help_web/live/request_live/show.ex:1038
#, 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:503
+#: lib/who_need_help_web/live/activity_live/show.ex:507
#, elixir-autogen, elixir-format
msgid "Location privacy"
msgstr "Приватність геопозиції"
-#: lib/who_need_help_web/live/request_live/show.ex:914
+#: lib/who_need_help_web/live/request_live/show.ex:927
#, 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:312
+#: lib/who_need_help_web/live/request_live/new.ex:317
#, elixir-autogen, elixir-format
msgid "Location visibility"
msgstr "Видимість геопозиції"
@@ -1085,12 +1076,12 @@ msgstr "Видимість геопозиції"
msgid "Location-supported people"
msgstr "Люди з підтвердженням геопозицією"
-#: lib/who_need_help_web/live/request_live/show.ex:717
+#: lib/who_need_help_web/live/request_live/show.ex:722
#, elixir-autogen, elixir-format
msgid "Location:"
msgstr "Геопозиція:"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:102
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:136
#, elixir-autogen, elixir-format
msgid "Log in and stay logged in"
msgstr "Увійти й залишатися в системі"
@@ -1100,23 +1091,18 @@ msgstr "Увійти й залишатися в системі"
msgid "Log in instructions"
msgstr "Інструкції для входу"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:139
#, elixir-autogen, elixir-format
msgid "Log in only this time"
msgstr "Увійти лише зараз"
-#: 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_controller.ex:117
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr "Ви успішно вийшли."
-#: lib/who_need_help_web/live/activity_live/new.ex:289
-#: lib/who_need_help_web/live/request_live/new.ex:347
+#: lib/who_need_help_web/live/activity_live/new.ex:294
+#: lib/who_need_help_web/live/request_live/new.ex:352
#, elixir-autogen, elixir-format
msgid "Longitude"
msgstr "Довгота"
@@ -1139,7 +1125,7 @@ msgstr ""
"Значок підтвердження з’являється лише після авторизації у відповідного "
"провайдера."
-#: lib/who_need_help_web/live/activity_live/show.ex:591
+#: lib/who_need_help_web/live/activity_live/show.ex:598
#, elixir-autogen, elixir-format
msgid "Mark completed"
msgstr "Позначити завершеною"
@@ -1151,12 +1137,12 @@ msgstr "Знайдіть помічника й узгодьте деталі"
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:637
#, elixir-autogen, elixir-format
msgid "Matched"
msgstr "Помічника знайдено"
-#: lib/who_need_help_web/live/request_live/new.ex:113
+#: lib/who_need_help_web/live/request_live/new.ex:118
#, elixir-autogen, elixir-format
msgid "Medicine is ready at the pharmacy"
msgstr "Ліки готові до отримання в аптеці"
@@ -1171,7 +1157,7 @@ msgstr "Замовлення ліків готове до отримання в
msgid "Medicine pickup"
msgstr "Доставка ліків"
-#: lib/who_need_help_web/live/request_live/new.ex:156
+#: lib/who_need_help_web/live/request_live/new.ex:161
#, 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 ""
@@ -1192,7 +1178,7 @@ msgstr ""
"лише схваленій групі й ідіть або надсилайте скаргу, якщо ситуація здається "
"небезпечною."
-#: lib/who_need_help_web/live/activity_live/show.ex:386
+#: lib/who_need_help_web/live/activity_live/show.ex:390
#, elixir-autogen, elixir-format
msgid "Meeting area"
msgstr "Область зустрічі"
@@ -1212,12 +1198,12 @@ msgstr "Об’єднати з"
msgid "Merge note"
msgstr "Примітка до об’єднання"
-#: lib/who_need_help_web/live/activity_live/show.ex:483
+#: lib/who_need_help_web/live/activity_live/show.ex:487
#, elixir-autogen, elixir-format
msgid "Message the approved group"
msgstr "Надіслати повідомлення схваленій групі"
-#: lib/who_need_help_web/live/category_proposal_live.ex:97
+#: lib/who_need_help_web/live/category_proposal_live.ex:103
#, elixir-autogen, elixir-format
msgid "Mode"
msgstr "Режим"
@@ -1251,7 +1237,7 @@ msgstr "ВЗАЄМОДОПОМОГА ПОРУЧ"
msgid "Nearby help"
msgstr "Допомога поруч"
-#: lib/who_need_help_web/live/request_live/new.ex:116
+#: lib/who_need_help_web/live/request_live/new.ex:121
#, elixir-autogen, elixir-format
msgid "Need roadside help in a safe location"
msgstr "Потрібна допомога на узбіччі дороги"
@@ -1261,15 +1247,15 @@ msgstr "Потрібна допомога на узбіччі дороги"
msgid "Network"
msgstr "Мережа"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:94
#, elixir-autogen, elixir-format
msgid "New password"
msgstr "Новий пароль"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:212
+#: lib/who_need_help_web/live/activity_live/show.ex:300
+#: lib/who_need_help_web/live/request_live/new.ex:260
+#: lib/who_need_help_web/live/request_live/show.ex:568
#, elixir-autogen, elixir-format
msgid "No"
msgstr "Ні"
@@ -1279,8 +1265,8 @@ msgstr "Ні"
msgid "No completed help yet."
msgstr "Завершеної допомоги поки немає."
-#: lib/who_need_help_web/live/activity_live/show.ex:451
-#: lib/who_need_help_web/live/request_live/show.ex:771
+#: lib/who_need_help_web/live/activity_live/show.ex:455
+#: lib/who_need_help_web/live/request_live/show.ex:776
#, elixir-autogen, elixir-format
msgid "No messages yet."
msgstr "Ще немає повідомлень."
@@ -1300,12 +1286,12 @@ msgstr "Відкритих заявок поки немає"
msgid "No open signals."
msgstr "Немає відкритих сигналів."
-#: lib/who_need_help_web/live/activity_live/show.ex:586
+#: lib/who_need_help_web/live/activity_live/show.ex:593
#, elixir-autogen, elixir-format
msgid "No pending join requests."
msgstr "Немає заявок на участь, що очікують."
-#: lib/who_need_help_web/live/category_proposal_live.ex:134
+#: lib/who_need_help_web/live/category_proposal_live.ex:140
#, elixir-autogen, elixir-format
msgid "No proposals yet."
msgstr "Пропозицій ще немає."
@@ -1340,9 +1326,9 @@ msgstr "Не для екстрених ситуацій"
#: lib/who_need_help_web/live/request_live/index.ex:168
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:298
+#: lib/who_need_help_web/live/request_live/show.ex:628
+#: lib/who_need_help_web/live/request_live/show.ex:629
#, elixir-autogen, elixir-format
msgid "Now"
msgstr "Зараз"
@@ -1372,7 +1358,7 @@ msgstr ""
msgid "Only the conversation linked to this report is displayed."
msgstr "Показано лише листування, пов’язане з цією скаргою."
-#: lib/who_need_help_web/live/activity_live/show.ex:437
+#: lib/who_need_help_web/live/activity_live/show.ex:441
#, elixir-autogen, elixir-format
msgid "Only the organizer and approved participants can read or send these messages."
msgstr ""
@@ -1380,11 +1366,11 @@ msgstr ""
"повідомлення."
#: 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/activity_live/show.ex:342
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:636
#, elixir-autogen, elixir-format
msgid "Open"
msgstr "Відкрита"
@@ -1394,7 +1380,7 @@ msgstr "Відкрита"
msgid "Open abuse signals"
msgstr "Відкриті сигнали про зловживання"
-#: lib/who_need_help_web/live/category_proposal_live.ex:128
+#: lib/who_need_help_web/live/category_proposal_live.ex:134
#, elixir-autogen, elixir-format
msgid "Open proposals"
msgstr "Відкриті пропозиції"
@@ -1404,50 +1390,50 @@ msgstr "Відкриті пропозиції"
msgid "Optional external thank-you link"
msgstr "Необов’язкове зовнішнє посилання для подяки"
-#: lib/who_need_help_web/live/request_live/show.ex:1011
+#: lib/who_need_help_web/live/request_live/show.ex:1024
#, elixir-autogen, elixir-format
msgid "Optional thank-you link"
msgstr "Необов’язкове посилання для подяки"
-#: lib/who_need_help_web/live/activity_live/show.ex:390
+#: lib/who_need_help_web/live/activity_live/show.ex:394
#, elixir-autogen, elixir-format
msgid "Organizer"
msgstr "Організатор"
-#: lib/who_need_help_web/live/activity_live/show.ex:155
+#: lib/who_need_help_web/live/activity_live/show.ex:159
#, elixir-autogen, elixir-format
msgid "Organizer blocked. Their activities and messages are hidden."
msgstr "Організатора заблоковано. Його активності й повідомлення приховано."
-#: lib/who_need_help_web/live/activity_live/show.ex:539
+#: lib/who_need_help_web/live/activity_live/show.ex:546
#, elixir-autogen, elixir-format
msgid "Organizer controls"
msgstr "Керування організатора"
-#: lib/who_need_help_web/live/activity_live/show.ex:399
+#: lib/who_need_help_web/live/activity_live/show.ex:403
#, elixir-autogen, elixir-format
msgid "Organizer social links"
msgstr "Посилання організатора на соцмережі"
-#: lib/who_need_help_web/live/activity_live/show.ex:667
+#: lib/who_need_help_web/live/activity_live/show.ex:679
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:1110
#, elixir-autogen, elixir-format
msgid "Other"
msgstr "Інше"
-#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:112
#, elixir-autogen, elixir-format
msgid "Parent (optional)"
msgstr "Батьківська категорія (необов’язково)"
-#: lib/who_need_help_web/live/activity_live/show.ex:459
+#: lib/who_need_help_web/live/activity_live/show.ex:463
#, elixir-autogen, elixir-format
msgid "Participant"
msgstr "Учасник"
-#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:97
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:130
#, elixir-autogen, elixir-format
msgid "Password"
msgstr "Пароль"
@@ -1457,27 +1443,27 @@ msgstr "Пароль"
msgid "Password updated successfully."
msgstr "Пароль успішно оновлено."
-#: lib/who_need_help_web/live/request_live/new.ex:280
+#: lib/who_need_help_web/live/request_live/new.ex:285
#, elixir-autogen, elixir-format
msgid "Pickup instructions (optional)"
msgstr "Інструкції для отримання (необов’язково)"
-#: lib/who_need_help_web/live/request_live/show.ex:686
+#: lib/who_need_help_web/live/request_live/show.ex:691
#, elixir-autogen, elixir-format
msgid "Pickup notes"
msgstr "Інструкції з отримання"
-#: lib/who_need_help_web/live/activity_live/new.ex:225
+#: lib/who_need_help_web/live/activity_live/new.ex:230
#, elixir-autogen, elixir-format
msgid "Plan and expectations"
msgstr "План і очікування"
-#: lib/who_need_help_web/live/activity_live/show.ex:421
+#: lib/who_need_help_web/live/activity_live/show.ex:425
#, elixir-autogen, elixir-format
msgid "Plan details"
msgstr "Деталі плану"
-#: lib/who_need_help_web/live/request_live/show.ex:619
+#: lib/who_need_help_web/live/request_live/show.ex:624
#, elixir-autogen, elixir-format
msgid "Please check the entered data."
msgstr "Будь ласка, перевірте введені дані."
@@ -1487,12 +1473,12 @@ msgstr "Будь ласка, перевірте введені дані."
msgid "Please check the submitted fields."
msgstr "Будь ласка, перевірте вказані поля."
-#: lib/who_need_help_web/live/activity_live/show.ex:334
+#: lib/who_need_help_web/live/activity_live/show.ex:338
#, elixir-autogen, elixir-format
msgid "Please check the submitted message."
msgstr "Будь ласка, перевірте надіслане повідомлення."
-#: lib/who_need_help_web/live/request_live/new.ex:307
+#: lib/who_need_help_web/live/request_live/new.ex:312
#, elixir-autogen, elixir-format
msgid "Podil, near the central pharmacy"
msgstr "Поділь, поблизу центральної аптеки."
@@ -1508,7 +1494,7 @@ msgstr "Опублікуйте зрозумілу заявку"
msgid "Primary navigation"
msgstr "Основна навігація"
-#: lib/who_need_help_web/live/request_live/show.ex:755
+#: lib/who_need_help_web/live/request_live/show.ex:760
#, elixir-autogen, elixir-format
msgid "Private match chat"
msgstr "Приватний чат учасників"
@@ -1526,7 +1512,7 @@ msgid "Profile updated."
msgstr "Профіль оновлено."
#: lib/who_need_help_web/live/moderation_live.ex:251
-#: lib/who_need_help_web/live/request_live/show.ex:1089
+#: lib/who_need_help_web/live/request_live/show.ex:1107
#, elixir-autogen, elixir-format
msgid "Prohibited item"
msgstr "Заборонений предмет"
@@ -1546,14 +1532,14 @@ msgstr "Пропозицію опубліковано для голосуван
msgid "Proposal rejected."
msgstr "Пропозицію відхилено."
-#: lib/who_need_help_web/live/category_proposal_live.ex:86
+#: lib/who_need_help_web/live/category_proposal_live.ex:87
#, elixir-autogen, elixir-format
msgid "Proposals and votes guide moderators. Approval remains a human decision."
msgstr ""
"Пропозиції та голоси допомагають модераторам, але рішення про схвалення "
"завжди ухвалює людина."
-#: lib/who_need_help_web/live/category_proposal_live.ex:91
+#: lib/who_need_help_web/live/category_proposal_live.ex:97
#, elixir-autogen, elixir-format
msgid "Proposed category"
msgstr "Пропонована категорія"
@@ -1570,7 +1556,7 @@ msgstr ""
" одним учасником іншого; похідні ознаки руху й близькості можуть "
"зберігатися."
-#: lib/who_need_help_web/live/activity_live/new.ex:256
+#: lib/who_need_help_web/live/activity_live/new.ex:261
#, elixir-autogen, elixir-format
msgid "Public map visibility"
msgstr "Видимість на публічній мапі"
@@ -1585,30 +1571,30 @@ msgstr "URL публічного профілю"
msgid "Public reputation"
msgstr "Публічна репутація"
-#: lib/who_need_help_web/live/activity_live/new.ex:117
+#: lib/who_need_help_web/live/activity_live/new.ex:122
#, 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:310
+#: lib/who_need_help_web/live/activity_live/new.ex:315
#, elixir-autogen, elixir-format
msgid "Publish activity"
msgstr "Опублікувати активність"
-#: lib/who_need_help_web/live/category_proposal_live.ex:123
+#: lib/who_need_help_web/live/category_proposal_live.ex:129
#, elixir-autogen, elixir-format
msgid "Publish proposal"
msgstr "Оприлюднити пропозицію"
-#: lib/who_need_help_web/live/request_live/new.ex:364
+#: lib/who_need_help_web/live/request_live/new.ex:369
#, elixir-autogen, elixir-format
msgid "Publish request"
msgstr "Опублікувати заявку"
-#: lib/who_need_help_web/live/activity_live/new.ex:308
-#: lib/who_need_help_web/live/request_live/new.ex:362
+#: lib/who_need_help_web/live/activity_live/new.ex:313
+#: lib/who_need_help_web/live/request_live/new.ex:367
#, elixir-autogen, elixir-format
msgid "Publishing…"
msgstr "Публікація…"
@@ -1637,17 +1623,17 @@ msgstr ""
"передачі. Повторна допомога між однією парою не збільшує основний показник."
#: lib/who_need_help_web/live/leaderboard_live.ex:60
-#: lib/who_need_help_web/live/request_live/show.ex:823
+#: lib/who_need_help_web/live/request_live/show.ex:836
#, elixir-autogen, elixir-format
msgid "Rating"
msgstr "Оцінка"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:40
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:77
#, elixir-autogen, elixir-format
msgid "Read the"
msgstr "Прочитайте"
-#: lib/who_need_help_web/live/activity_live/show.ex:660
+#: lib/who_need_help_web/live/activity_live/show.ex:672
#, elixir-autogen, elixir-format
msgid "Reason"
msgstr "Причина"
@@ -1672,22 +1658,22 @@ msgstr "Примітка про відхилення"
msgid "Remove"
msgstr "Вилучити"
-#: lib/who_need_help_web/live/activity_live/show.ex:468
+#: lib/who_need_help_web/live/activity_live/show.ex:472
#, elixir-autogen, elixir-format
msgid "Report"
msgstr "Поскаржитися"
-#: lib/who_need_help_web/live/request_live/show.ex:789
+#: lib/who_need_help_web/live/request_live/show.ex:794
#, elixir-autogen, elixir-format
msgid "Report message"
msgstr "Поскаржитися на повідомлення"
-#: lib/who_need_help_web/live/request_live/show.ex:1084
+#: lib/who_need_help_web/live/request_live/show.ex:1102
#, elixir-autogen, elixir-format
msgid "Report reason"
msgstr "Причина скарги"
-#: lib/who_need_help_web/live/activity_live/show.ex:123
+#: lib/who_need_help_web/live/activity_live/show.ex:126
#: lib/who_need_help_web/live/request_live/show.ex:261
#, elixir-autogen, elixir-format
msgid "Report sent to moderators."
@@ -1708,7 +1694,7 @@ msgstr "Матеріали зі скарги"
msgid "Reports"
msgstr "Скарги"
-#: lib/who_need_help_web/live/request_live/show.ex:1053
+#: lib/who_need_help_web/live/request_live/show.ex:1066
#, elixir-autogen, elixir-format
msgid "Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages."
msgstr ""
@@ -1720,7 +1706,7 @@ msgstr ""
msgid "Request cancelled."
msgstr "Заявку скасовано."
-#: lib/who_need_help_web/live/request_live/new.ex:301
+#: lib/who_need_help_web/live/request_live/new.ex:306
#, elixir-autogen, elixir-format
msgid "Request expires (UTC)"
msgstr "Запит закінчується (UTC)"
@@ -1735,22 +1721,22 @@ msgstr "Заявку приховано."
msgid "Request restored."
msgstr "Заявку відновлено."
-#: lib/who_need_help_web/live/activity_live/show.ex:521
+#: lib/who_need_help_web/live/activity_live/show.ex:525
#, elixir-autogen, elixir-format
msgid "Request to join"
msgstr "Подати заявку на участь"
-#: lib/who_need_help_web/live/request_live/show.ex:725
+#: lib/who_need_help_web/live/request_live/show.ex:730
#, elixir-autogen, elixir-format
msgid "Requester links:"
msgstr "Посилання заявника:"
-#: lib/who_need_help_web/live/request_live/show.ex:706
+#: lib/who_need_help_web/live/request_live/show.ex:711
#, elixir-autogen, elixir-format
msgid "Requester:"
msgstr "Заявник:"
-#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:745
#, elixir-autogen, elixir-format
msgid "Requester: %{count} completed"
msgstr "Автор заявки: %{count} завершено"
@@ -1778,6 +1764,7 @@ msgid "Review note"
msgstr "Примітка до перевірки"
#: lib/who_need_help_web/live/request_live/show.ex:233
+#: lib/who_need_help_web/live/request_live/show.ex:825
#, elixir-autogen, elixir-format
msgid "Review saved. It appears after both participants review."
msgstr ""
@@ -1811,8 +1798,8 @@ msgstr "Опис російською (необов’язково)"
msgid "Russian name (optional)"
msgstr "Назва російською (необов’язково)"
-#: lib/who_need_help_web/live/activity_live/show.ex:635
-#: lib/who_need_help_web/live/request_live/show.ex:1051
+#: lib/who_need_help_web/live/activity_live/show.ex:642
+#: lib/who_need_help_web/live/request_live/show.ex:1064
#, elixir-autogen, elixir-format
msgid "Safety controls"
msgstr "Інструменти безпеки"
@@ -1838,7 +1825,7 @@ msgstr "Правила безпеки"
msgid "Save"
msgstr "Зберегти"
-#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:48
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:108
#, elixir-autogen, elixir-format
msgid "Save Password"
msgstr "Зберегти пароль"
@@ -1851,21 +1838,21 @@ msgstr "Зберегти профіль"
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:300
+#: lib/who_need_help_web/live/request_live/show.ex:632
+#: lib/who_need_help_web/live/request_live/show.ex:633
#, elixir-autogen, elixir-format
msgid "Scheduled"
msgstr "Заплановано"
-#: lib/who_need_help_web/live/activity_live/show.ex:486
-#: lib/who_need_help_web/live/request_live/show.ex:806
+#: lib/who_need_help_web/live/activity_live/show.ex:490
+#: lib/who_need_help_web/live/request_live/show.ex:811
#, elixir-autogen, elixir-format
msgid "Send"
msgstr "Надіслати"
-#: lib/who_need_help_web/live/activity_live/show.ex:676
-#: lib/who_need_help_web/live/request_live/show.ex:1101
+#: lib/who_need_help_web/live/activity_live/show.ex:688
+#: lib/who_need_help_web/live/request_live/show.ex:1119
#, elixir-autogen, elixir-format
msgid "Send report"
msgstr "Надіслати скаргу"
@@ -1877,12 +1864,12 @@ msgstr ""
"Установіть CODEX_SESSION_ID у середовищі розгортання перед записом "
"демонстрації."
-#: lib/who_need_help_web/live/request_live/show.ex:1038
+#: lib/who_need_help_web/live/request_live/show.ex:1051
#, elixir-autogen, elixir-format
msgid "Share location"
msgstr "Поділитися геопозицією"
-#: lib/who_need_help_web/live/request_live/new.ex:282
+#: lib/who_need_help_web/live/request_live/new.ex:287
#, elixir-autogen, elixir-format
msgid "Share only non-sensitive instructions. Exact details can wait for the matched chat."
msgstr ""
@@ -1894,8 +1881,8 @@ msgstr ""
msgid "Short bio"
msgstr "Коротко про себе"
-#: lib/who_need_help_web/live/activity_live/new.ex:219
-#: lib/who_need_help_web/live/request_live/new.ex:266
+#: lib/who_need_help_web/live/activity_live/new.ex:224
+#: lib/who_need_help_web/live/request_live/new.ex:271
#, elixir-autogen, elixir-format
msgid "Short title"
msgstr "Короткий заголовок"
@@ -1925,8 +1912,8 @@ msgstr "Слаг"
msgid "Social activities"
msgstr "Спільні активності"
-#: lib/who_need_help_web/live/category_proposal_live.ex:100
-#: lib/who_need_help_web/live/category_proposal_live.ex:143
+#: lib/who_need_help_web/live/category_proposal_live.ex:106
+#: lib/who_need_help_web/live/category_proposal_live.ex:149
#, elixir-autogen, elixir-format
msgid "Social activity"
msgstr "Спільна активність"
@@ -1966,24 +1953,24 @@ msgstr "Сеанс підтвердження соцмережі заверши
msgid "Social verification session is invalid."
msgstr "Сеанс соціальної перевірки є некоректним."
-#: lib/who_need_help_web/live/activity_live/show.ex:665
+#: lib/who_need_help_web/live/activity_live/show.ex:677
#: lib/who_need_help_web/live/moderation_live.ex:252
-#: lib/who_need_help_web/live/request_live/show.ex:1090
+#: lib/who_need_help_web/live/request_live/show.ex:1108
#, elixir-autogen, elixir-format
msgid "Spam"
msgstr "Спам"
-#: lib/who_need_help_web/live/request_live/show.ex:940
+#: lib/who_need_help_web/live/request_live/show.ex:953
#, elixir-autogen, elixir-format
msgid "Start helping"
msgstr "Почати допомагати"
-#: lib/who_need_help_web/live/activity_live/show.ex:374
+#: lib/who_need_help_web/live/activity_live/show.ex:378
#, elixir-autogen, elixir-format
msgid "Starts"
msgstr "Початок"
-#: lib/who_need_help_web/live/activity_live/new.ex:234
+#: lib/who_need_help_web/live/activity_live/new.ex:239
#, elixir-autogen, elixir-format
msgid "Starts at (UTC)"
msgstr "Початок (UTC)"
@@ -1993,7 +1980,7 @@ msgstr "Початок (UTC)"
msgid "Status"
msgstr "Статус"
-#: lib/who_need_help_web/live/request_live/show.ex:1046
+#: lib/who_need_help_web/live/request_live/show.ex:1059
#, elixir-autogen, elixir-format
msgid "Stop and delete position"
msgstr "Зупинити й видалити геопозицію"
@@ -2008,7 +1995,7 @@ msgstr "Структуровані поля JSON"
msgid "Structured fields must be a valid JSON object."
msgstr "Структуровані поля мають бути коректним об’єктом JSON."
-#: lib/who_need_help_web/live/request_live/show.ex:837
+#: lib/who_need_help_web/live/request_live/show.ex:850
#, elixir-autogen, elixir-format
msgid "Submit review"
msgstr "Надіслати відгук"
@@ -2019,13 +2006,13 @@ msgstr "Надіслати відгук"
msgid "Suspended"
msgstr "Заблоковано"
-#: lib/who_need_help_web/live/request_live/show.ex:604
+#: lib/who_need_help_web/live/request_live/show.ex:609
#, elixir-autogen, elixir-format
msgid "That handover code is not correct."
msgstr "Неправильний код передачі."
-#: lib/who_need_help_web/live/activity_live/show.ex:140
-#: lib/who_need_help_web/live/request_live/show.ex:278
+#: lib/who_need_help_web/live/activity_live/show.ex:144
+#: lib/who_need_help_web/live/request_live/show.ex:279
#, elixir-autogen, elixir-format
msgid "That message cannot be reported from this view."
msgstr "На це повідомлення не можна поскаржитися з цього екрана."
@@ -2035,7 +2022,7 @@ msgstr "На це повідомлення не можна поскаржити
msgid "That social account is already linked."
msgstr "Цей обліковий запис соцмережі вже прив’язано."
-#: lib/who_need_help_web/live/request_live/show.ex:608
+#: lib/who_need_help_web/live/request_live/show.ex:613
#, elixir-autogen, elixir-format
msgid "That step is not available in the current state."
msgstr "Цей крок недоступний у поточному стані."
@@ -2050,7 +2037,7 @@ msgstr "Провайдер перевірки не налаштовано."
msgid "That verification provider is not supported."
msgstr "Цей постачальник перевірки не підтримується."
-#: lib/who_need_help_web/live/activity_live/show.ex:312
+#: lib/who_need_help_web/live/activity_live/show.ex:316
#, elixir-autogen, elixir-format
msgid "The approved group has reached its capacity."
msgstr "Схвалена група вже заповнена."
@@ -2070,7 +2057,7 @@ msgstr "Останній адміністратор не може понизит
msgid "The link is invalid or it has expired."
msgstr "Посилання недійсне або прострочене."
-#: lib/who_need_help_web/live/activity_live/show.ex:526
+#: lib/who_need_help_web/live/activity_live/show.ex:530
#, elixir-autogen, elixir-format
msgid "The organizer has not approved your request yet."
msgstr "Організатор ще не схвалив ваш запит."
@@ -2088,52 +2075,52 @@ msgstr ""
"Платформа ніколи не обробляє, не вимагає, не розподіляє й не гарантує "
"оплату."
-#: lib/who_need_help_web/live/activity_live/show.ex:508
+#: lib/who_need_help_web/live/activity_live/show.ex:512
#, 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:648
+#: lib/who_need_help_web/live/activity_live/show.ex:660
#, 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:1076
+#: lib/who_need_help_web/live/request_live/show.ex:1094
#, 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:315
+#: lib/who_need_help_web/live/activity_live/show.ex:319
#, elixir-autogen, elixir-format
msgid "This action is unavailable."
msgstr "Ця дія недоступна."
-#: lib/who_need_help_web/live/activity_live/show.ex:322
+#: lib/who_need_help_web/live/activity_live/show.ex:326
#, elixir-autogen, elixir-format
msgid "This activity is no longer open."
msgstr "Ця активність більше не відкрита."
-#: lib/who_need_help_web/live/activity_live/show.ex:320
+#: lib/who_need_help_web/live/activity_live/show.ex:324
#, elixir-autogen, elixir-format
msgid "This activity state has already changed."
msgstr "Цей стан активності вже змінено."
-#: lib/who_need_help_web/live/request_live/show.ex:613
+#: lib/who_need_help_web/live/request_live/show.ex:618
#, elixir-autogen, elixir-format
msgid "This interaction is blocked."
msgstr "Ця взаємодія заблокована."
#: lib/who_need_help_web/live/request_live/index.ex:210
-#: lib/who_need_help_web/live/request_live/show.ex:665
+#: lib/who_need_help_web/live/request_live/show.ex:670
#, 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:164
+#: lib/who_need_help_web/live/request_live/new.ex:169
#, 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 ""
@@ -2146,7 +2133,7 @@ msgstr ""
msgid "This proposal has already been reviewed."
msgstr "Цю пропозицію вже розглянуто."
-#: lib/who_need_help_web/live/category_proposal_live.ex:56
+#: lib/who_need_help_web/live/category_proposal_live.ex:57
#, elixir-autogen, elixir-format
msgid "This proposal is already closed."
msgstr "Ця пропозиція вже закрита."
@@ -2156,13 +2143,13 @@ msgstr "Ця пропозиція вже закрита."
msgid "This report has no linked conversation."
msgstr "Ця скарга не має пов’язаного листування."
-#: lib/who_need_help_web/live/request_live/show.ex:1016
+#: lib/who_need_help_web/live/request_live/show.ex:1029
#, elixir-autogen, elixir-format
msgid "This request already has a helper."
msgstr "Ця заявка вже має помічника."
#: lib/who_need_help_web/live/request_live/show.ex:74
-#: lib/who_need_help_web/live/request_live/show.ex:615
+#: lib/who_need_help_web/live/request_live/show.ex:620
#, elixir-autogen, elixir-format
msgid "This request is no longer available."
msgstr "Ця заявка більше не доступна."
@@ -2180,28 +2167,29 @@ msgstr "Щоб переглянути надіслані листи, відкр
#: 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
+#: lib/who_need_help_web/live/request_live/new.ex:299
+#: lib/who_need_help_web/live/request_live/show.ex:630
+#: lib/who_need_help_web/live/request_live/show.ex:631
#, elixir-autogen, elixir-format
msgid "Today"
msgstr "Сьогодні"
-#: 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/activity_live/new.ex:108
+#: lib/who_need_help_web/live/activity_live/show.ex:336
+#: lib/who_need_help_web/live/category_proposal_live.ex:55
#: lib/who_need_help_web/live/profile_live.ex:59
-#: lib/who_need_help_web/live/request_live/show.ex:618
+#: lib/who_need_help_web/live/request_live/show.ex:623
#, elixir-autogen, elixir-format
msgid "Too many actions in the configured time window."
msgstr "Забагато дій протягом налаштованого проміжку часу."
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:180
#: 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:142
+#: lib/who_need_help_web/live/request_live/new.ex:147
#, elixir-autogen, elixir-format
msgid "Too many requests in the configured time window."
msgstr "Забагато запитів протягом налаштованого проміжку часу."
@@ -2211,7 +2199,7 @@ msgstr "Забагато запитів протягом налаштовано
msgid "Too many sign-in emails in the configured time window."
msgstr "Забагато листів для входу протягом налаштованого проміжку часу."
-#: lib/who_need_help_web/live/category_proposal_live.ex:107
+#: lib/who_need_help_web/live/category_proposal_live.ex:113
#, elixir-autogen, elixir-format
msgid "Top level"
msgstr "Верхній рівень"
@@ -2221,7 +2209,7 @@ msgstr "Верхній рівень"
msgid "Total"
msgstr "Загалом"
-#: lib/who_need_help_web/live/activity_live/new.ex:246
+#: lib/who_need_help_web/live/activity_live/new.ex:251
#, elixir-autogen, elixir-format
msgid "Total group capacity"
msgstr "Загальний розмір групи"
@@ -2241,7 +2229,7 @@ msgstr "Назва українською (необов’язково)"
msgid "Unblock"
msgstr "Розблокувати"
-#: lib/who_need_help_web/live/request_live/show.ex:1069
+#: lib/who_need_help_web/live/request_live/show.ex:1082
#, elixir-autogen, elixir-format
msgid "Unblock this user"
msgstr "Розблокувати цього користувача"
@@ -2257,18 +2245,18 @@ msgid "Update email instructions"
msgstr "Інструкції для зміни електронної пошти"
#: lib/who_need_help_web/live/request_live/index.ex:240
-#: lib/who_need_help_web/live/request_live/new.ex:291
+#: lib/who_need_help_web/live/request_live/new.ex:296
#, elixir-autogen, elixir-format
msgid "Urgency"
msgstr "Терміновість"
-#: lib/who_need_help_web/live/category_proposal_live.ex:99
-#: lib/who_need_help_web/live/category_proposal_live.ex:142
+#: lib/who_need_help_web/live/category_proposal_live.ex:105
+#: lib/who_need_help_web/live/category_proposal_live.ex:148
#, elixir-autogen, elixir-format
msgid "Urgent help"
msgstr "Термінова допомога"
-#: lib/who_need_help_web/live/activity_live/new.ex:123
+#: lib/who_need_help_web/live/activity_live/new.ex:128
#, 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 ""
@@ -2285,8 +2273,8 @@ msgstr "Увімкнути темну тему"
msgid "Use light theme"
msgstr "Увімкнути світлу тему"
-#: lib/who_need_help_web/live/activity_live/new.ex:271
-#: lib/who_need_help_web/live/request_live/new.ex:329
+#: lib/who_need_help_web/live/activity_live/new.ex:276
+#: lib/who_need_help_web/live/request_live/new.ex:334
#, elixir-autogen, elixir-format
msgid "Use my current foreground location"
msgstr "Використати мою поточну геопозицію"
@@ -2310,7 +2298,7 @@ msgstr "Використати системну тему"
msgid "User"
msgstr "Користувач"
-#: lib/who_need_help_web/live/request_live/show.ex:307
+#: lib/who_need_help_web/live/request_live/show.ex:308
#, elixir-autogen, elixir-format
msgid "User blocked. Their requests and new messages are hidden."
msgstr "Користувача заблоковано. Його заявки й нові повідомлення приховано."
@@ -2331,7 +2319,7 @@ msgid "User status updated."
msgstr "Статус користувача оновлено."
#: lib/who_need_help_web/live/profile_live.ex:87
-#: lib/who_need_help_web/live/request_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:322
#, elixir-autogen, elixir-format
msgid "User unblocked."
msgstr "Користувача розблоковано."
@@ -2346,7 +2334,7 @@ msgstr "Підтверджені люди"
msgid "Verify GitHub"
msgstr "Підтвердити через GitHub"
-#: lib/who_need_help_web/live/request_live/show.ex:996
+#: lib/who_need_help_web/live/request_live/show.ex:1009
#, elixir-autogen, elixir-format
msgid "Verify handover"
msgstr "Підтвердити передачу"
@@ -2366,11 +2354,12 @@ msgstr "Переглянути пов’язані докази"
msgid "Visible reviews"
msgstr "Відкриті відгуки"
-#: lib/who_need_help_web/live/request_live/show.ex:866
+#: lib/who_need_help_web/live/request_live/show.ex:879
#, elixir-autogen, elixir-format
msgid "Waiting for a nearby volunteer."
msgstr "Очікуємо помічника поруч."
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:137
#: 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
@@ -2382,18 +2371,18 @@ msgstr "З поверненням!"
msgid "What Codex contributed"
msgstr "Що зробив Codex"
-#: lib/who_need_help_web/live/activity_live/show.ex:673
-#: lib/who_need_help_web/live/request_live/show.ex:1098
+#: lib/who_need_help_web/live/activity_live/show.ex:685
+#: lib/who_need_help_web/live/request_live/show.ex:1116
#, elixir-autogen, elixir-format
msgid "What happened?"
msgstr "Що сталося?"
-#: lib/who_need_help_web/live/category_proposal_live.ex:83
+#: lib/who_need_help_web/live/category_proposal_live.ex:84
#, elixir-autogen, elixir-format
msgid "What help category is missing?"
msgstr "Яка категорія допомоги відсутня?"
-#: lib/who_need_help_web/live/request_live/new.ex:272
+#: lib/who_need_help_web/live/request_live/new.ex:277
#, elixir-autogen, elixir-format
msgid "What help do you need?"
msgstr "Яка допомога вам потрібна?"
@@ -2419,7 +2408,7 @@ msgstr ""
msgid "Who Need Help home"
msgstr "Головна Who Need Help"
-#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:53
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:90
#, 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 ""
@@ -2439,40 +2428,40 @@ msgstr ""
msgid "Who needs help?"
msgstr "Хто потребує допомоги?"
-#: lib/who_need_help_web/live/category_proposal_live.ex:121
+#: lib/who_need_help_web/live/category_proposal_live.ex:127
#, elixir-autogen, elixir-format
msgid "Why is this useful?"
msgstr "Чому це корисно?"
-#: lib/who_need_help_web/live/request_live/show.ex:957
+#: lib/who_need_help_web/live/request_live/show.ex:970
#, elixir-autogen, elixir-format
msgid "Withdraw from this request"
msgstr "Відмовитися від цієї заявки"
-#: lib/who_need_help_web/live/request_live/show.ex:802
+#: lib/who_need_help_web/live/request_live/show.ex:807
#, elixir-autogen, elixir-format
msgid "Write a safe coordination message…"
msgstr "Напишіть безпечне повідомлення для координації…"
-#: 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
+#: lib/who_need_help_web/live/activity_live/new.ex:209
+#: lib/who_need_help_web/live/activity_live/show.ex:298
+#: lib/who_need_help_web/live/request_live/new.ex:257
+#: lib/who_need_help_web/live/request_live/show.ex:566
#, elixir-autogen, elixir-format
msgid "Yes"
msgstr "Так"
-#: lib/who_need_help_web/live/activity_live/show.ex:309
+#: lib/who_need_help_web/live/activity_live/show.ex:313
#, 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:58
+#: lib/who_need_help_web/live/category_proposal_live.ex:59
#, elixir-autogen, elixir-format
msgid "You already voted for this proposal."
msgstr "Ви вже проголосували за цю пропозицію."
-#: lib/who_need_help_web/live/activity_live/show.ex:506
+#: lib/who_need_help_web/live/activity_live/show.ex:510
#, elixir-autogen, elixir-format
msgid "You are approved, so the map can show the exact meeting coordinates."
msgstr "Вас схвалено, тому карта може показувати точні координати зустрічі."
@@ -2482,8 +2471,8 @@ msgstr "Вас схвалено, тому карта може показуват
msgid "You are matched. Use chat to coordinate safely."
msgstr "Помічника призначено. Безпечно узгодьте деталі в чаті."
-#: lib/who_need_help_web/live/activity_live/show.ex:317
-#: lib/who_need_help_web/live/request_live/show.ex:605
+#: lib/who_need_help_web/live/activity_live/show.ex:321
+#: lib/who_need_help_web/live/request_live/show.ex:610
#, elixir-autogen, elixir-format
msgid "You are not allowed to do that."
msgstr "Ви не маєте дозволу на цю дію."
@@ -2493,7 +2482,7 @@ msgstr "Ви не маєте дозволу на цю дію."
msgid "You are running the local mail adapter."
msgstr "Використовується локальний поштовий адаптер."
-#: lib/who_need_help_web/live/request_live/new.ex:332
+#: lib/who_need_help_web/live/request_live/new.ex:337
#, elixir-autogen, elixir-format
msgid "You can also enter coordinates manually for local testing."
msgstr "Ви також можете ввести координати вручну для локального тестування."
@@ -2503,12 +2492,12 @@ msgstr "Ви також можете ввести координати вруч
msgid "You can be the first person to ask the community."
msgstr "Ви можете першими звернутися по допомогу до спільноти."
-#: lib/who_need_help_web/live/request_live/show.ex:602
+#: lib/who_need_help_web/live/request_live/show.ex:607
#, elixir-autogen, elixir-format
msgid "You cannot accept your own request."
msgstr "Не можна прийняти власну заявку."
-#: lib/who_need_help_web/live/activity_live/show.ex:316
+#: lib/who_need_help_web/live/activity_live/show.ex:320
#, elixir-autogen, elixir-format
msgid "You cannot report your own content."
msgstr "Не можна скаржитися на власний вміст."
@@ -2563,7 +2552,7 @@ msgstr "Ваше підтвердження збережено."
msgid "Your profile"
msgstr "Ваш профіль"
-#: lib/who_need_help_web/live/request_live/new.ex:47
+#: lib/who_need_help_web/live/request_live/new.ex:52
#, elixir-autogen, elixir-format
msgid "Your request is now visible to nearby helpers."
msgstr "Тепер вашу заявку бачать помічники поруч."
@@ -2573,7 +2562,7 @@ msgstr "Тепер вашу заявку бачать помічники пор
msgid "Your requests"
msgstr "Ваші запити"
-#: lib/who_need_help_web/live/request_live/show.ex:817
+#: lib/who_need_help_web/live/request_live/show.ex:822
#, elixir-autogen, elixir-format
msgid "Your review is revealed only after both participants submit."
msgstr ""
@@ -2594,13 +2583,13 @@ msgstr "активність"
msgid "activity message"
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:79
#, elixir-autogen, elixir-format
msgid "before confirming."
msgstr "перед підтвердженням."
#: 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/category_proposal_live.ex:153
#: 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
@@ -2609,7 +2598,7 @@ msgstr "автор: %{name}"
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:896
#, elixir-autogen, elixir-format
msgid "completed"
msgstr "завершено"
@@ -2624,59 +2613,54 @@ msgstr "спливає %{time}"
msgid "for an account now."
msgstr "обліковий запис зараз."
-#: lib/who_need_help_web/live/request_live/show.ex:910
+#: lib/who_need_help_web/live/request_live/show.ex:923
#, elixir-autogen, elixir-format
msgid "helper movement supported"
msgstr "рух помічника підтверджено"
-#: lib/who_need_help_web/live/request_live/show.ex:911
+#: lib/who_need_help_web/live/request_live/show.ex:924
#, elixir-autogen, elixir-format
msgid "no movement evidence"
msgstr "немає підтвердження руху"
-#: lib/who_need_help_web/live/request_live/show.ex:902
+#: lib/who_need_help_web/live/request_live/show.ex:915
#, elixir-autogen, elixir-format
msgid "no proximity evidence"
msgstr "немає підтвердження близькості"
-#: 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:605
+#: lib/who_need_help_web/live/activity_live/show.ex:612
#, elixir-autogen, elixir-format
msgid "organizer"
msgstr "організатор"
-#: lib/who_need_help_web/live/request_live/show.ex:756
+#: lib/who_need_help_web/live/request_live/show.ex:761
#, elixir-autogen, elixir-format
msgid "participants only"
msgstr "лише учасники"
#: lib/who_need_help_web/live/request_live/index.ex:328
-#: lib/who_need_help_web/live/request_live/show.ex:887
+#: lib/who_need_help_web/live/request_live/show.ex:900
#, elixir-autogen, elixir-format
msgid "people"
msgstr "люди"
-#: lib/who_need_help_web/live/request_live/show.ex:901
+#: lib/who_need_help_web/live/request_live/show.ex:914
#, elixir-autogen, elixir-format
msgid "proximity supported"
msgstr "близькість підтверджено"
#: lib/who_need_help_web/live/profile_live.ex:204
-#: lib/who_need_help_web/live/request_live/show.ex:891
+#: lib/who_need_help_web/live/request_live/show.ex:904
#, elixir-autogen, elixir-format
msgid "rating"
msgstr "оцінка"
-#: lib/who_need_help_web/live/request_live/show.ex:748
+#: lib/who_need_help_web/live/request_live/show.ex:753
#, elixir-autogen, elixir-format
msgid "rating %{rating}"
msgstr "оцінка %{rating}"
-#: 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:78
#, elixir-autogen, elixir-format
msgid "safety rules"
msgstr "правила безпеки"
@@ -2696,12 +2680,12 @@ msgstr "до свого облікового запису зараз."
msgid "unique people"
msgstr "унікальні люди"
-#: 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/activity_live/show.ex:418
+#: lib/who_need_help_web/live/activity_live/show.ex:572
+#: lib/who_need_help_web/live/activity_live/show.ex:631
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:740
+#: lib/who_need_help_web/live/request_live/show.ex:945
#, elixir-autogen, elixir-format
msgid "unverified"
msgstr "не підтверджено"
@@ -2711,13 +2695,13 @@ msgstr "не підтверджено"
msgid "urgent"
msgstr "терміново"
-#: 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/activity_live/show.ex:417
+#: lib/who_need_help_web/live/activity_live/show.ex:571
+#: lib/who_need_help_web/live/activity_live/show.ex:630
#: 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
+#: lib/who_need_help_web/live/request_live/show.ex:739
+#: lib/who_need_help_web/live/request_live/show.ex:944
#, elixir-autogen, elixir-format
msgid "verified"
msgstr "підтверджено"
@@ -2727,18 +2711,18 @@ msgstr "підтверджено"
msgid "verified handovers"
msgstr "підтверджені передачі"
-#: lib/who_need_help_web/live/request_live/show.ex:660
+#: lib/who_need_help_web/live/request_live/show.ex:665
#, elixir-autogen, elixir-format
msgid "← All requests"
msgstr "← Усі заявки"
-#: lib/who_need_help_web/live/activity_live/new.ex:113
-#: lib/who_need_help_web/live/activity_live/show.ex:348
+#: lib/who_need_help_web/live/activity_live/new.ex:118
+#: lib/who_need_help_web/live/activity_live/show.ex:352
#, elixir-autogen, elixir-format
msgid "← Back to activities"
msgstr "← Назад до активностей"
-#: lib/who_need_help_web/live/request_live/new.ex:152
+#: lib/who_need_help_web/live/request_live/new.ex:157
#, elixir-autogen, elixir-format
msgid "← Back to requests"
msgstr "← Назад до заявок"
@@ -2799,15 +2783,15 @@ msgstr "Повторювана пара учасників"
msgid "Unusual action rate"
msgstr "Незвична частота дій"
-#: lib/who_need_help_web/live/activity_live/show.ex:448
-#: lib/who_need_help_web/live/request_live/show.ex:768
+#: lib/who_need_help_web/live/activity_live/show.ex:452
+#: lib/who_need_help_web/live/request_live/show.ex:773
#, elixir-autogen, elixir-format
msgid "Load earlier messages"
msgstr "Завантажити попередні повідомлення"
#: 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/category_proposal_live.ex:168
#: lib/who_need_help_web/live/leaderboard_live.ex:87
#: lib/who_need_help_web/live/moderation_live.ex:384
#: lib/who_need_help_web/live/moderation_live.ex:419
@@ -2822,9 +2806,9 @@ msgid "Load more"
msgstr "Завантажити ще"
#: 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/activity_live/show.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:314
-#: lib/who_need_help_web/live/request_live/show.ex:849
+#: lib/who_need_help_web/live/request_live/show.ex:862
#, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable."
msgstr "Карта недоступна в цьому браузері. Решта інформації залишається доступною."
@@ -2845,19 +2829,19 @@ msgstr "Підтвердьте новий email для свого обліков
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
+#: lib/who_need_help_web/live/activity_live/show.ex:340
+#: lib/who_need_help_web/live/category_proposal_live.ex:61
+#: lib/who_need_help_web/live/request_live/show.ex:626
#, 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
+#: lib/who_need_help_web/live/activity_live/new.ex:110
#, 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
+#: lib/who_need_help_web/live/request_live/new.ex:149
#, elixir-autogen, elixir-format
msgid "Could not publish the request. Please try again."
msgstr "Не вдалося опублікувати заявку. Спробуйте ще раз."
@@ -2872,32 +2856,32 @@ msgstr "Не вдалося розблокувати користувача. С
msgid "JavaScript is required to confirm this protected email link."
msgstr "Для підтвердження цього захищеного посилання на email потрібен JavaScript."
-#: lib/who_need_help_web/live/request_live/show.ex:594
+#: lib/who_need_help_web/live/request_live/show.ex:599
#, elixir-autogen, elixir-format
msgid "Last update: %{time}"
msgstr "Останнє оновлення: %{time}"
-#: lib/who_need_help_web/live/request_live/show.ex:617
+#: lib/who_need_help_web/live/request_live/show.ex:622
#, elixir-autogen, elixir-format
msgid "Live location sharing is not active."
msgstr "Передавання геопозиції зараз не активне."
-#: lib/who_need_help_web/live/request_live/show.ex:592
+#: lib/who_need_help_web/live/request_live/show.ex:597
#, elixir-autogen, elixir-format
msgid "Shared location"
msgstr "Передавана геопозиція"
-#: lib/who_need_help_web/live/activity_live/show.ex:329
+#: lib/who_need_help_web/live/activity_live/show.ex:333
#, elixir-autogen, elixir-format
msgid "The organizer cannot leave the activity."
msgstr "Організатор не може залишити активність."
-#: lib/who_need_help_web/live/activity_live/show.ex:326
+#: lib/who_need_help_web/live/activity_live/show.ex:330
#, elixir-autogen, elixir-format
msgid "The organizer is already attending."
msgstr "Організатор уже бере участь."
-#: lib/who_need_help_web/controllers/user_registration_controller.ex:60
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:58
#, elixir-autogen, elixir-format
msgid "The registration form is invalid."
msgstr "Форму реєстрації заповнено неправильно."
@@ -2917,22 +2901,22 @@ msgstr "Форму налаштувань заповнено неправиль
msgid "The sign-in form is invalid."
msgstr "Форму входу заповнено неправильно."
-#: lib/who_need_help_web/live/activity_live/show.ex:323
+#: lib/who_need_help_web/live/activity_live/show.ex:327
#, elixir-autogen, elixir-format
msgid "This activity is no longer available."
msgstr "Ця активність більше недоступна."
-#: lib/who_need_help_web/live/request_live/show.ex:616
+#: lib/who_need_help_web/live/request_live/show.ex:621
#, elixir-autogen, elixir-format
msgid "This match is no longer active."
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 "This proposal is no longer available."
msgstr "Ця пропозиція більше недоступна."
-#: lib/who_need_help_web/live/request_live/show.ex:614
+#: lib/who_need_help_web/live/request_live/show.ex:619
#, elixir-autogen, elixir-format
msgid "This request has expired."
msgstr "Термін дії цієї заявки минув."
@@ -2947,12 +2931,12 @@ msgstr "Забагато спроб входу протягом налаштов
msgid "Your secure email link is ready. Continue to sign in."
msgstr "Захищене посилання з листа готове. Продовжте вхід."
-#: lib/who_need_help_web/controllers/user_settings_controller.ex:128
+#: lib/who_need_help_web/controllers/user_settings_controller.ex:130
#, 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
+#: lib/who_need_help_web/controllers/user_registration_controller.ex:107
#, elixir-autogen, elixir-format
msgid "If this address can be registered or signed in, login instructions will arrive shortly."
msgstr "Якщо цю адресу можна зареєструвати або використати для входу, інструкції для входу незабаром надійдуть."
@@ -2960,9 +2944,191 @@ msgstr "Якщо цю адресу можна зареєструвати або
#: lib/who_need_help_web/live/profile_live.ex:62
#, elixir-autogen, elixir-format
msgid "This account cannot perform that action."
-msgstr ""
+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 протягом налаштованого проміжку часу."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:32
+#, elixir-autogen, elixir-format
+msgid "A Google account is connected. You can use it to sign in."
+msgstr "Обліковий запис Google підключено. Тепер за його допомогою можна входити."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:207
+#, elixir-autogen, elixir-format
+msgid "A different Google account is already connected."
+msgstr "Уже підключено інший обліковий запис Google."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:17
+#, elixir-autogen, elixir-format
+msgid "A password is not required. We will email a one-time confirmation link. You can add a password later in account settings."
+msgstr "Пароль не обов’язковий. Ми надішлемо на email одноразове посилання для підтвердження. Пароль можна додати пізніше в налаштуваннях облікового запису."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:170
+#, elixir-autogen, elixir-format
+msgid "An account with this email already exists. Sign in by email or password, then connect Google in account settings."
+msgstr "Обліковий запис із таким email уже існує. Увійдіть за email або паролем, а потім підключіть Google у налаштуваннях облікового запису."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:37
+#, elixir-autogen, elixir-format
+msgid "Confirm that you are 18 or older and accept the safety rules first."
+msgstr "Спочатку підтвердьте, що вам уже виповнилося 18 років, і прийміть правила безпеки."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:49
+#, elixir-autogen, elixir-format
+msgid "Connect Google account"
+msgstr "Підключити обліковий запис Google"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:34
+#, elixir-autogen, elixir-format
+msgid "Connect Google only after signing in here. We never merge accounts automatically by matching email addresses."
+msgstr "Підключайте Google лише після входу тут. Ми ніколи не об’єднуємо облікові записи автоматично за збігом email."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "Create account and email me a link"
+msgstr "Створити обліковий запис і надіслати посилання на email"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:99
+#, elixir-autogen, elixir-format
+msgid "Email me a sign-in link"
+msgstr "Надіслати посилання для входу на email"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:215
+#, elixir-autogen, elixir-format
+msgid "Google account connection session is invalid."
+msgstr "Сесія підключення облікового запису Google недійсна."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:97
+#, elixir-autogen, elixir-format
+msgid "Google did not provide a verified email address."
+msgstr "Google не надав підтверджену адресу email."
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:29
+#, elixir-autogen, elixir-format
+msgid "Google sign-in"
+msgstr "Вхід через Google"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:197
+#, elixir-autogen, elixir-format
+msgid "Google sign-in connected."
+msgstr "Вхід через Google підключено."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:123
+#, elixir-autogen, elixir-format
+msgid "Google sign-in could not be started."
+msgstr "Не вдалося розпочати вхід через Google."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:102
+#, elixir-autogen, elixir-format
+msgid "Google sign-in failed. Please try again."
+msgstr "Не вдалося увійти через Google. Спробуйте ще раз."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:128
+#, elixir-autogen, elixir-format
+msgid "Google sign-in is not configured yet."
+msgstr "Вхід через Google ще не налаштовано."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:87
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session expired. Please start again."
+msgstr "Сесія входу через Google завершилася. Почніть заново."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:91
+#, elixir-autogen, elixir-format
+msgid "Google sign-in session is invalid."
+msgstr "Сесія входу через Google недійсна."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:47
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:78
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:53
+#, elixir-autogen, elixir-format
+msgid "Google sign-in will be available after the operator configures it."
+msgstr "Вхід через Google стане доступним після налаштування оператором."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:95
+#, elixir-autogen, elixir-format
+msgid "Sending link..."
+msgstr "Надсилаємо посилання..."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:74
+#, elixir-autogen, elixir-format
+msgid "Sign in with Google"
+msgstr "Увійти через Google"
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:43
+#, elixir-autogen, elixir-format
+msgid "Sign up with Google"
+msgstr "Зареєструватися через Google"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:202
+#, elixir-autogen, elixir-format
+msgid "That Google account belongs to another local account."
+msgstr "Цей обліковий запис Google належить іншому локальному обліковому запису."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:45
+#, elixir-autogen, elixir-format
+msgid "The Google registration form is invalid."
+msgstr "Форму реєстрації через Google заповнено неправильно."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:260
+#, elixir-autogen, elixir-format
+msgid "This Google account cannot be used right now."
+msgstr "Цей обліковий запис Google зараз не можна використовувати."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:144
+#, elixir-autogen, elixir-format
+msgid "This Google account is not connected yet. Create an account or sign in by email and connect Google in account settings."
+msgstr "Цей обліковий запис Google ще не підключено. Створіть обліковий запис або увійдіть за email і підключіть Google у налаштуваннях."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:109
+#, elixir-autogen, elixir-format
+msgid "This works only if you previously added a password in account settings."
+msgstr "Це працює, лише якщо ви раніше додали пароль у налаштуваннях облікового запису."
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:105
+#, elixir-autogen, elixir-format
+msgid "Use a password instead"
+msgstr "Увійти за паролем"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:96
+#, elixir-autogen, elixir-format
+msgid "We will send a one-time sign-in link. No password is required."
+msgstr "Ми надішлемо одноразове посилання для входу. Пароль не потрібен."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:163
+#, elixir-autogen, elixir-format
+msgid "Your account was created with Google."
+msgstr "Ваш обліковий запис створено через Google."
+
+#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:51
+#, elixir-autogen, elixir-format
+msgid "or sign up with email"
+msgstr "або зареєструватися за email"
+
+#: lib/who_need_help_web/controllers/user_session_html/new.html.heex:82
+#, elixir-autogen, elixir-format
+msgid "or use email"
+msgstr "або використати email"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:70
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google"
+msgstr "Відключити Google"
+
+#: lib/who_need_help_web/controllers/user_settings_html/edit.html.heex:68
+#, elixir-autogen, elixir-format
+msgid "Disconnect Google sign-in from this account?"
+msgstr "Відключити вхід через Google для цього облікового запису?"
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:60
+#, elixir-autogen, elixir-format
+msgid "Google sign-in disconnected."
+msgstr "Вхід через Google відключено."
+
+#: lib/who_need_help_web/controllers/google_auth_controller.ex:65
+#, elixir-autogen, elixir-format
+msgid "No Google account is connected."
+msgstr "Обліковий запис Google не підключено."
diff --git a/priv/repo/migrations/20260720200749_create_auth_identities.exs b/priv/repo/migrations/20260720200749_create_auth_identities.exs
new file mode 100644
index 0000000..f096de4
--- /dev/null
+++ b/priv/repo/migrations/20260720200749_create_auth_identities.exs
@@ -0,0 +1,18 @@
+defmodule WhoNeedHelp.Repo.Migrations.CreateAuthIdentities do
+ use Ecto.Migration
+
+ def change do
+ create table(:auth_identities, primary_key: false) do
+ add :id, :binary_id, primary_key: true
+ add :provider, :string, null: false
+ add :provider_uid, :string, null: false
+ add :email, :string, null: false
+ add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false
+
+ timestamps(type: :utc_datetime)
+ end
+
+ create unique_index(:auth_identities, [:provider, :provider_uid])
+ create unique_index(:auth_identities, [:user_id, :provider])
+ end
+end
diff --git a/scripts/clean-deploy-verify.sh b/scripts/clean-deploy-verify.sh
index bb50865..d2aaa5f 100755
--- a/scripts/clean-deploy-verify.sh
+++ b/scripts/clean-deploy-verify.sh
@@ -154,6 +154,8 @@ PHX_URL_PORT=80
MAP_TILE_URL=https://tile.openstreetmap.org/{z}/{x}/{y}.png
GITHUB_OAUTH_CLIENT_ID=
GITHUB_OAUTH_CLIENT_SECRET=
+GOOGLE_OAUTH_CLIENT_ID=
+GOOGLE_OAUTH_CLIENT_SECRET=
PUSH_HTTP_ENDPOINT=
PUSH_HTTP_BEARER_TOKEN=
POSTGRES_DB=$postgres_db
diff --git a/scripts/ensure-local-e2e-env.sh b/scripts/ensure-local-e2e-env.sh
index 7d2c991..b0bf22f 100755
--- a/scripts/ensure-local-e2e-env.sh
+++ b/scripts/ensure-local-e2e-env.sh
@@ -85,6 +85,8 @@ CODEX_SESSION_ID=local-e2e
RATE_LIMIT_POLICIES_JSON={}
GITHUB_OAUTH_CLIENT_ID=
GITHUB_OAUTH_CLIENT_SECRET=
+GOOGLE_OAUTH_CLIENT_ID=
+GOOGLE_OAUTH_CLIENT_SECRET=
E2E_BASE_URL=https://proxy
E2E_MAILPIT_URL=http://mailpit:8025
E2E_OUTPUT_DIR=$ROOT/output/e2e/generated-per-run
diff --git a/scripts/external-boundaries-run.sh b/scripts/external-boundaries-run.sh
index 563dbdc..0801bcc 100755
--- a/scripts/external-boundaries-run.sh
+++ b/scripts/external-boundaries-run.sh
@@ -146,6 +146,12 @@ jq -e '
.oauth.fresh_flow_retry_after_temporary_failure == "passed" and
.oauth.timeout_failed_closed == true and
.oauth.access_token_returned_to_application == false and
+ .google_oidc.success == "passed" and
+ .google_oidc.state_mismatch_blocked_before_token == true and
+ .google_oidc.one_time_code_replay_rejected == true and
+ .google_oidc.nonce_mismatch_rejected_after_signature_verification == true and
+ .google_oidc.unverified_email_rejected == true and
+ .google_oidc.access_token_returned_to_application == false and
.smtp.success == "passed" and
.smtp.permanent_rejection_not_retried == true and
.smtp.temporary_greeting_retried_once == true and
diff --git a/scripts/quality.sh b/scripts/quality.sh
index cc07334..d682a27 100755
--- a/scripts/quality.sh
+++ b/scripts/quality.sh
@@ -103,6 +103,16 @@ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \
./scripts/init-production-env.sh help.test "$production_env" >/dev/null
test "$(stat -c '%a' "$production_env")" = 600
./scripts/validate-production-env.sh "$production_env" help.test >/dev/null
+partial_google_env="$scan_dir/.env.production.partial-google"
+cp "$production_env" "$partial_google_env"
+chmod 600 "$partial_google_env"
+sed -i 's/^GOOGLE_OAUTH_CLIENT_ID=.*/GOOGLE_OAUTH_CLIENT_ID=quality-client/' \
+ "$partial_google_env"
+if ./scripts/validate-production-env.sh \
+ "$partial_google_env" help.test >/dev/null 2>&1; then
+ echo "Production environment validator accepted partial Google OAuth credentials." >&2
+ exit 1
+fi
if ./scripts/init-production-env.sh help.test "$production_env" >/dev/null 2>&1; then
echo "Production environment initializer overwrote an existing file." >&2
exit 1
diff --git a/scripts/rotate-local-secrets.sh b/scripts/rotate-local-secrets.sh
index 0688aed..f42c4f2 100755
--- a/scripts/rotate-local-secrets.sh
+++ b/scripts/rotate-local-secrets.sh
@@ -4,6 +4,15 @@ set -eu
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
ENV_FILE="$ROOT/.env"
TEST_IMAGE="${TEST_IMAGE:-who-need-help:test}"
+MODE=${1:-all}
+
+case "$MODE" in
+ all | --database-only) ;;
+ *)
+ echo "Usage: $0 [--database-only]" >&2
+ exit 1
+ ;;
+esac
if [ ! -f "$ENV_FILE" ]; then
echo "Missing $ENV_FILE. Copy .env.example to .env first." >&2
@@ -37,9 +46,6 @@ generate_phoenix_secret() {
docker run --rm "$TEST_IMAGE" mix phx.gen.secret
}
-new_secret_key_base=$(generate_phoenix_secret)
-new_handover_secret=$(generate_phoenix_secret)
-new_release_cookie=$(generate_phoenix_secret)
new_postgres_password=$(openssl rand -hex 32)
new_database_url="ecto://${POSTGRES_USER}:${new_postgres_password}@db/${POSTGRES_DB}"
@@ -47,18 +53,31 @@ tmp_env=$(mktemp "${ENV_FILE}.rotate.XXXXXX")
trap 'rm -f "$tmp_env"' EXIT HUP INT TERM
chmod 600 "$tmp_env"
-NEW_SECRET_KEY_BASE=$new_secret_key_base \
-NEW_HANDOVER_SECRET=$new_handover_secret \
-NEW_RELEASE_COOKIE=$new_release_cookie \
-NEW_POSTGRES_PASSWORD=$new_postgres_password \
-NEW_DATABASE_URL=$new_database_url \
- perl -pe '
- s/^SECRET_KEY_BASE=.*/SECRET_KEY_BASE=$ENV{NEW_SECRET_KEY_BASE}/;
- s/^HANDOVER_SECRET=.*/HANDOVER_SECRET=$ENV{NEW_HANDOVER_SECRET}/;
- s/^RELEASE_COOKIE=.*/RELEASE_COOKIE=$ENV{NEW_RELEASE_COOKIE}/;
- s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$ENV{NEW_POSTGRES_PASSWORD}/;
- s/^DATABASE_URL=.*/DATABASE_URL=$ENV{NEW_DATABASE_URL}/;
- ' "$ENV_FILE" >"$tmp_env"
+if [ "$MODE" = "--database-only" ]; then
+ NEW_POSTGRES_PASSWORD=$new_postgres_password \
+ NEW_DATABASE_URL=$new_database_url \
+ perl -pe '
+ s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$ENV{NEW_POSTGRES_PASSWORD}/;
+ s/^DATABASE_URL=.*/DATABASE_URL=$ENV{NEW_DATABASE_URL}/;
+ ' "$ENV_FILE" >"$tmp_env"
+else
+ new_secret_key_base=$(generate_phoenix_secret)
+ new_handover_secret=$(generate_phoenix_secret)
+ new_release_cookie=$(generate_phoenix_secret)
+
+ NEW_SECRET_KEY_BASE=$new_secret_key_base \
+ NEW_HANDOVER_SECRET=$new_handover_secret \
+ NEW_RELEASE_COOKIE=$new_release_cookie \
+ NEW_POSTGRES_PASSWORD=$new_postgres_password \
+ NEW_DATABASE_URL=$new_database_url \
+ perl -pe '
+ s/^SECRET_KEY_BASE=.*/SECRET_KEY_BASE=$ENV{NEW_SECRET_KEY_BASE}/;
+ s/^HANDOVER_SECRET=.*/HANDOVER_SECRET=$ENV{NEW_HANDOVER_SECRET}/;
+ s/^RELEASE_COOKIE=.*/RELEASE_COOKIE=$ENV{NEW_RELEASE_COOKIE}/;
+ s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$ENV{NEW_POSTGRES_PASSWORD}/;
+ s/^DATABASE_URL=.*/DATABASE_URL=$ENV{NEW_DATABASE_URL}/;
+ ' "$ENV_FILE" >"$tmp_env"
+fi
printf "ALTER ROLE \"%s\" WITH PASSWORD '%s';\n" \
"$POSTGRES_USER" "$new_postgres_password" |
@@ -70,5 +89,9 @@ mv "$tmp_env" "$ENV_FILE"
chmod 600 "$ENV_FILE"
trap - EXIT HUP INT TERM
-echo "Local application and PostgreSQL secrets rotated without printing their values."
+if [ "$MODE" = "--database-only" ]; then
+ echo "Local PostgreSQL password rotated without printing its value."
+else
+ echo "Local application and PostgreSQL secrets rotated without printing their values."
+fi
echo "Run docker compose up -d --wait to apply the new application environment."
diff --git a/scripts/validate-production-env.sh b/scripts/validate-production-env.sh
index 11eaec8..cf6d1f8 100755
--- a/scripts/validate-production-env.sh
+++ b/scripts/validate-production-env.sh
@@ -41,6 +41,12 @@ read_value() {
' "$env_file"
}
+optional_value() {
+ local key=$1
+
+ read_value "$key" 2>/dev/null || true
+}
+
require_value() {
local key=$1
local value
@@ -80,10 +86,14 @@ release_cookie=$(require_value RELEASE_COOKIE)
metrics_token=$(require_value METRICS_TOKEN)
smtp_relay=$(require_value SMTP_RELAY)
smtp_port=$(require_value SMTP_PORT)
+smtp_username=$(optional_value SMTP_USERNAME)
+smtp_password=$(optional_value SMTP_PASSWORD)
smtp_auth=$(require_value SMTP_AUTH)
smtp_tls=$(require_value SMTP_TLS)
smtp_ssl=$(require_value SMTP_SSL)
email_from_address=$(require_value EMAIL_FROM_ADDRESS)
+google_oauth_client_id=$(optional_value GOOGLE_OAUTH_CLIENT_ID)
+google_oauth_client_secret=$(optional_value GOOGLE_OAUTH_CLIENT_SECRET)
[[ "$phx_host" == "$expected_domain" ]] || {
echo "PHX_HOST does not match EXPECTED_DOMAIN." >&2
@@ -137,11 +147,35 @@ done
echo "SMTP_SSL has an unsupported value." >&2
exit 1
}
+if [[ -n "$smtp_username" || -n "$smtp_password" ]]; then
+ [[ -n "$smtp_username" && -n "$smtp_password" ]] || {
+ echo "SMTP username and password must either both be set or both be empty." >&2
+ exit 1
+ }
+fi
+if [[ "$smtp_auth" == always && (-z "$smtp_username" || -z "$smtp_password") ]]; then
+ echo "SMTP username and password are required when SMTP_AUTH is always." >&2
+ exit 1
+fi
+if [[ "$smtp_ssl" =~ ^(true|1)$ && "$smtp_tls" != never ]]; then
+ echo "SMTP_TLS must be never when SMTP_SSL enables an implicit TLS connection." >&2
+ exit 1
+fi
[[ "$email_from_address" == *@* ]] || {
echo "EMAIL_FROM_ADDRESS is not an email address." >&2
exit 1
}
+if [[ -n "$google_oauth_client_id" || -n "$google_oauth_client_secret" ]]; then
+ [[ -n "$google_oauth_client_id" && -n "$google_oauth_client_secret" ]] || {
+ echo "Google OAuth client ID and secret must either both be set or both be empty." >&2
+ exit 1
+ }
+
+ reject_marker GOOGLE_OAUTH_CLIENT_ID "$google_oauth_client_id"
+ reject_marker GOOGLE_OAUTH_CLIENT_SECRET "$google_oauth_client_secret"
+fi
+
secrets=(
"$postgres_password"
"$secret_key_base"
diff --git a/test/support/google_auth_fake.ex b/test/support/google_auth_fake.ex
new file mode 100644
index 0000000..da368ca
--- /dev/null
+++ b/test/support/google_auth_fake.ex
@@ -0,0 +1,46 @@
+defmodule WhoNeedHelp.GoogleAuthFake do
+ @behaviour WhoNeedHelp.GoogleAuth
+
+ @impl true
+ def enabled?, do: true
+
+ @impl true
+ def authorize_url(redirect_uri) do
+ {:ok,
+ %{
+ url:
+ "https://accounts.google.example/authorize?" <>
+ URI.encode_query(%{"redirect_uri" => redirect_uri, "state" => "google-test-state"}),
+ session_params: %{
+ state: "google-test-state",
+ nonce: "google-test-nonce",
+ code_verifier: "google-test-verifier"
+ }
+ }}
+ end
+
+ @impl true
+ def callback(_redirect_uri, params, %{state: state}) do
+ cond do
+ params["state"] != state ->
+ {:error, :invalid_state}
+
+ params["code"] in [nil, ""] ->
+ {:error, :missing_code}
+
+ params["email_verified"] == "false" ->
+ {:error, :email_not_verified}
+
+ true ->
+ email = params["email"] || "google-user@example.com"
+
+ {:ok,
+ %{
+ provider_uid: params["uid"] || "google-user-42",
+ email: email,
+ email_verified: true,
+ display_name: params["name"] || "Google Neighbor"
+ }}
+ end
+ end
+end
diff --git a/test/who_need_help/google_auth_test.exs b/test/who_need_help/google_auth_test.exs
new file mode 100644
index 0000000..31cc682
--- /dev/null
+++ b/test/who_need_help/google_auth_test.exs
@@ -0,0 +1,71 @@
+defmodule WhoNeedHelp.GoogleAuthTest do
+ use ExUnit.Case, async: true
+
+ alias WhoNeedHelp.GoogleAuth.AssentAdapter
+
+ test "Google authorization uses OIDC state, nonce, PKCE, and identity-only scopes" do
+ nonce = "session-bound-nonce"
+
+ assert {:ok, %{url: url, session_params: session_params}} =
+ Assent.Strategy.Google.authorize_url(
+ client_id: "client",
+ client_secret: "secret",
+ redirect_uri: "https://example.test/auth/google/callback",
+ nonce: nonce,
+ code_verifier: true,
+ openid_configuration: %{
+ "authorization_endpoint" => "https://accounts.google.test/o/oauth2/v2/auth"
+ }
+ )
+
+ uri = URI.parse(url)
+ params = URI.decode_query(uri.query)
+
+ assert uri.host == "accounts.google.test"
+ assert params["redirect_uri"] == "https://example.test/auth/google/callback"
+ assert params["scope"] == "openid email profile"
+ assert params["state"] == session_params.state
+ assert params["nonce"] == nonce
+ assert session_params.nonce == nonce
+ assert params["code_challenge_method"] == "S256"
+ assert is_binary(session_params.code_verifier)
+ refute Map.has_key?(params, "access_type")
+ end
+
+ test "normalizes only a verified Google identity and discards token-shaped claims" do
+ assert {:ok, identity} =
+ AssentAdapter.normalize_identity(%{
+ "sub" => "google-subject-123",
+ "email" => " Alice@Example.COM ",
+ "email_verified" => true,
+ "name" => "Alice Neighbor",
+ "access_token" => "must-not-leak",
+ "id_token" => "must-not-leak"
+ })
+
+ assert identity == %{
+ provider_uid: "google-subject-123",
+ email: "alice@example.com",
+ email_verified: true,
+ display_name: "Alice Neighbor"
+ }
+
+ refute Map.has_key?(identity, :access_token)
+ refute Map.has_key?(identity, :id_token)
+ end
+
+ test "rejects an unverified or incomplete Google email" do
+ assert {:error, :email_not_verified} =
+ AssentAdapter.normalize_identity(%{
+ "sub" => "subject",
+ "email" => "alice@example.com",
+ "email_verified" => false
+ })
+
+ assert {:error, :invalid_provider_identity} =
+ AssentAdapter.normalize_identity(%{
+ "sub" => "subject",
+ "email_verified" => true
+ })
+ end
+end
diff --git a/test/who_need_help_web/controllers/google_auth_controller_test.exs b/test/who_need_help_web/controllers/google_auth_controller_test.exs
new file mode 100644
index 0000000..48c4727
--- /dev/null
+++ b/test/who_need_help_web/controllers/google_auth_controller_test.exs
@@ -0,0 +1,263 @@
+defmodule WhoNeedHelpWeb.GoogleAuthControllerTest do
+ use WhoNeedHelpWeb.ConnCase, async: false
+
+ alias WhoNeedHelp.Accounts
+ alias WhoNeedHelp.Accounts.AuthIdentity
+ alias WhoNeedHelp.Repo
+ alias WhoNeedHelp.Trust.AuditEvent
+
+ import WhoNeedHelp.AccountsFixtures
+
+ test "starts a login flow with callback state, nonce, and PKCE session data", %{conn: conn} do
+ conn = post(conn, ~p"/auth/google/login")
+
+ assert redirected_to(conn) =~ "https://accounts.google.example/authorize?"
+
+ assert %{
+ "flow" => "login",
+ "session_params" => %{
+ state: "google-test-state",
+ nonce: "google-test-nonce",
+ code_verifier: "google-test-verifier"
+ }
+ } = get_session(conn, "google_auth_flow")
+ end
+
+ test "requires adult and safety consent before starting Google registration", %{conn: conn} do
+ conn =
+ post(conn, ~p"/auth/google/register", %{
+ "google_registration" => %{"locale" => "en", "terms_accepted" => "false"}
+ })
+
+ assert redirected_to(conn) == ~p"/users/register"
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "18 or older"
+ assert is_nil(get_session(conn, "google_auth_flow"))
+ end
+
+ test "registers, confirms, links, and logs in a new verified Google user", %{conn: conn} do
+ email = unique_user_email()
+
+ conn =
+ conn
+ |> post(~p"/auth/google/register", %{
+ "google_registration" => %{"locale" => "ru", "terms_accepted" => "true"}
+ })
+ |> recycle()
+ |> get(
+ ~p"/auth/google/callback?code=one-time-code&state=google-test-state&uid=google-123&email=#{email}&name=Google%20Helper"
+ )
+
+ assert redirected_to(conn) == ~p"/"
+ assert get_session(conn, :user_token)
+ assert is_nil(get_session(conn, "google_auth_flow"))
+
+ user = Accounts.get_user_by_email(email)
+ assert user.confirmed_at
+ assert user.accepted_terms_at
+ assert user.locale == "ru"
+ assert user.display_name == "Google Helper"
+ assert is_nil(user.hashed_password)
+
+ assert %AuthIdentity{
+ provider: :google,
+ provider_uid: "google-123",
+ email: ^email,
+ user_id: user_id
+ } = Repo.get_by(AuthIdentity, provider: :google, provider_uid: "google-123")
+
+ assert user_id == user.id
+ end
+
+ test "does not automatically merge an existing email account", %{conn: conn} do
+ user = user_fixture()
+
+ conn =
+ conn
+ |> post(~p"/auth/google/register", %{
+ "google_registration" => %{"locale" => "en", "terms_accepted" => "true"}
+ })
+ |> recycle()
+ |> get(
+ ~p"/auth/google/callback?code=code&state=google-test-state&uid=new-google&email=#{user.email}"
+ )
+
+ assert redirected_to(conn) == ~p"/users/log-in"
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "already exists"
+ refute get_session(conn, :user_token)
+ refute Repo.get_by(AuthIdentity, provider: :google, provider_uid: "new-google")
+ end
+
+ test "logs in only through an identity already linked to the local account", %{conn: conn} do
+ user = user_fixture()
+
+ assert {:ok, _identity} =
+ Accounts.link_google_identity(user, %{
+ provider_uid: "returning-google",
+ email: user.email,
+ email_verified: true,
+ display_name: user.display_name
+ })
+
+ conn =
+ conn
+ |> post(~p"/auth/google/login")
+ |> recycle()
+ |> get(
+ ~p"/auth/google/callback?code=code&state=google-test-state&uid=returning-google&email=#{user.email}"
+ )
+
+ assert redirected_to(conn) == ~p"/"
+ assert get_session(conn, :user_token)
+ end
+
+ test "does not create an account from the login-only flow", %{conn: conn} do
+ email = unique_user_email()
+
+ conn =
+ conn
+ |> post(~p"/auth/google/login")
+ |> recycle()
+ |> get(
+ ~p"/auth/google/callback?code=code&state=google-test-state&uid=unknown-google&email=#{email}"
+ )
+
+ assert redirected_to(conn) == ~p"/users/log-in"
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "not connected"
+ refute Accounts.get_user_by_email(email)
+ refute get_session(conn, :user_token)
+ end
+
+ test "rejects unverified Google email and consumes the flow session", %{conn: conn} do
+ conn =
+ conn
+ |> post(~p"/auth/google/login")
+ |> recycle()
+ |> get(~p"/auth/google/callback?code=code&state=google-test-state&email_verified=false")
+
+ assert redirected_to(conn) == ~p"/users/log-in"
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "verified email"
+ assert is_nil(get_session(conn, "google_auth_flow"))
+ end
+
+ test "callback cannot be replayed", %{conn: conn} do
+ first =
+ conn
+ |> post(~p"/auth/google/login")
+ |> recycle()
+ |> get(~p"/auth/google/callback?code=code&state=google-test-state")
+
+ replay =
+ first
+ |> recycle()
+ |> get(~p"/auth/google/callback?code=code&state=google-test-state")
+
+ assert redirected_to(replay) == ~p"/users/log-in"
+ assert Phoenix.Flash.get(replay.assigns.flash, :error) =~ "session expired"
+ end
+
+ test "links Google only to the authenticated flow owner and records an audit event", %{
+ conn: conn
+ } do
+ user = user_fixture()
+
+ conn =
+ conn
+ |> log_in_user(user)
+ |> post(~p"/auth/google/link")
+ |> recycle()
+ |> get(
+ ~p"/auth/google/callback?code=code&state=google-test-state&uid=linked-google&email=linked@example.com"
+ )
+
+ assert redirected_to(conn) == ~p"/users/settings"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "connected"
+
+ identity = Repo.get_by!(AuthIdentity, provider: :google, provider_uid: "linked-google")
+ assert identity.user_id == user.id
+
+ assert %AuditEvent{actor_id: actor_id, target_id: target_id} =
+ Repo.get_by!(AuditEvent,
+ action: "auth_identity.connected",
+ target_id: identity.id
+ )
+
+ assert actor_id == user.id
+ assert target_id == identity.id
+ end
+
+ test "rejects a link callback after the authenticated account changes", %{conn: conn} do
+ first_user = user_fixture()
+ second_user = user_fixture()
+
+ conn =
+ conn
+ |> log_in_user(first_user)
+ |> post(~p"/auth/google/link")
+ |> recycle()
+ |> log_in_user(second_user)
+ |> get(~p"/auth/google/callback?code=code&state=google-test-state&uid=wrong-owner")
+
+ assert redirected_to(conn) == ~p"/users/log-in"
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "invalid"
+ refute Repo.get_by(AuthIdentity, provider: :google, provider_uid: "wrong-owner")
+ end
+
+ test "requires authentication to start account linking", %{conn: conn} do
+ conn = post(conn, ~p"/auth/google/link")
+ assert redirected_to(conn) == ~p"/users/log-in"
+ end
+
+ test "rejects a link callback after the sudo window expires", %{conn: conn} do
+ user = user_fixture()
+
+ conn =
+ conn
+ |> log_in_user(user)
+ |> post(~p"/auth/google/link")
+
+ token = get_session(conn, :user_token)
+
+ override_token_authenticated_at(
+ token,
+ DateTime.utc_now(:second) |> DateTime.add(-11, :minute)
+ )
+
+ conn =
+ conn
+ |> recycle()
+ |> get(~p"/auth/google/callback?code=code&state=google-test-state&uid=stale-google")
+
+ assert redirected_to(conn) == ~p"/users/log-in"
+ refute Repo.get_by(AuthIdentity, provider: :google, provider_uid: "stale-google")
+ end
+
+ test "disconnects Google in sudo mode and records the audit event", %{conn: conn} do
+ user = user_fixture()
+
+ assert {:ok, identity} =
+ Accounts.link_google_identity(user, %{
+ provider_uid: "disconnect-google",
+ email: user.email,
+ email_verified: true,
+ display_name: user.display_name
+ })
+
+ conn =
+ conn
+ |> log_in_user(user)
+ |> delete(~p"/auth/google/link")
+
+ assert redirected_to(conn) == ~p"/users/settings"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "disconnected"
+ refute Repo.get(AuthIdentity, identity.id)
+
+ assert %AuditEvent{actor_id: actor_id, target_id: target_id} =
+ Repo.get_by!(AuditEvent,
+ action: "auth_identity.disconnected",
+ target_id: identity.id
+ )
+
+ assert actor_id == user.id
+ assert target_id == identity.id
+ end
+end
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 6ea287b..8dead4d 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
@@ -12,6 +12,9 @@ defmodule WhoNeedHelpWeb.UserRegistrationControllerTest do
assert response =~ "Register"
assert response =~ ~p"/users/log-in"
assert response =~ ~p"/users/register"
+ assert response =~ "A password is not required"
+ assert response =~ "Sign up with Google"
+ assert response =~ ~p"/auth/google/register"
end
test "redirects if already logged in", %{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 a4f20e3..24c64a5 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
@@ -16,7 +16,9 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
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 =~ "Email me a sign-in link"
+ assert response =~ "Sign in with Google"
+ assert response =~ "Use a password instead"
assert response =~ ~s(id="magic-link-fragment-form")
assert response =~ ~s(hidden)
end
@@ -30,7 +32,7 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
assert html =~ "You need to reauthenticate"
refute html =~ "Register"
- assert html =~ "Log in with email"
+ assert html =~ "Email me a sign-in link"
assert html =~
~s(