Replace map grids with hierarchical clustering

This commit is contained in:
SimpleTest 2026-07-29 02:54:12 +03:00
parent 762b72100d
commit ee6a1f7e70
20 changed files with 1583 additions and 1022 deletions

View File

@ -1,8 +1,10 @@
import maplibregl from "maplibre-gl" import maplibregl from "maplibre-gl"
import { import {
clusterCountLabel,
clusterExpansionTarget,
clusterSizeClass, clusterSizeClass,
mergeNearbyMapPoints pointFeatureCollection
} from "./map_cluster_points.mjs" } from "./map_cluster_features.mjs"
const trackingMinTimeMs = 5000 const trackingMinTimeMs = 5000
@ -152,22 +154,6 @@ const areaFeatureCollection = points => ({
.map(areaPolygon) .map(areaPolygon)
}) })
const clusterFeatureCollection = points => ({
type: "FeatureCollection",
features: points
.filter(point => point.type === "cluster")
.map(point => ({
type: "Feature",
id: point.id,
properties: {
id: point.id,
count: point.count,
urgent_count: point.urgent_count || 0
},
geometry: {type: "Point", coordinates: [point.longitude, point.latitude]}
}))
})
const mapViewport = map => { const mapViewport = map => {
const bounds = map.getBounds() const bounds = map.getBounds()
const container = map.getContainer() const container = map.getContainer()
@ -300,7 +286,10 @@ const createAidMap = element => {
resizeObserver: null, resizeObserver: null,
markers: [], markers: [],
markerElements: new Map(), markerElements: new Map(),
points: [],
selectedItemId: null,
clusterExpansionPending: false, clusterExpansionPending: false,
pendingMarkerRender: false,
initializedViewport: false, initializedViewport: false,
locationBounds: null, locationBounds: null,
recenterControl: null, recenterControl: null,
@ -310,7 +299,9 @@ const createAidMap = element => {
areaSelectedId: `${element.id || "aid-map"}-area-selected`, areaSelectedId: `${element.id || "aid-map"}-area-selected`,
clusterSourceId: `${element.id || "aid-map"}-clusters`, clusterSourceId: `${element.id || "aid-map"}-clusters`,
clusterCircleId: `${element.id || "aid-map"}-cluster-circle`, clusterCircleId: `${element.id || "aid-map"}-cluster-circle`,
clusterCountId: `${element.id || "aid-map"}-cluster-count`, serverClusterCircleId: `${element.id || "aid-map"}-server-cluster-circle`,
pointCircleId: `${element.id || "aid-map"}-point-circle`,
pointSelectedId: `${element.id || "aid-map"}-point-selected`,
statusBadge: element.parentElement?.querySelector("[data-map-status-badge]") || null statusBadge: element.parentElement?.querySelector("[data-map-status-badge]") || null
} }
@ -458,6 +449,8 @@ const createAidMap = element => {
} }
state.highlightRequest = id => { state.highlightRequest = id => {
state.selectedItemId = id || null
state.markerElements.forEach((element, requestId) => { state.markerElements.forEach((element, requestId) => {
element.classList.toggle("is-selected", requestId === id) element.classList.toggle("is-selected", requestId === id)
}) })
@ -465,6 +458,10 @@ const createAidMap = element => {
if (state.map?.getLayer(state.areaSelectedId)) { if (state.map?.getLayer(state.areaSelectedId)) {
state.map.setFilter(state.areaSelectedId, ["==", ["get", "id"], id || ""]) state.map.setFilter(state.areaSelectedId, ["==", ["get", "id"], id || ""])
} }
if (state.map?.getLayer(state.pointSelectedId)) {
state.map.setFilter(state.pointSelectedId, ["==", ["get", "id"], id || ""])
}
} }
state.selectRequest = id => { state.selectRequest = id => {
@ -486,7 +483,6 @@ const createAidMap = element => {
const current = state.element.getBoundingClientRect() const current = state.element.getBoundingClientRect()
if (current.width >= 160 && current.height >= 160) { if (current.width >= 160 && current.height >= 160) {
state.map.resize() state.map.resize()
if (state.element.dataset.discoveryMap === "true") state.renderMarkers()
} }
}) })
} }
@ -517,6 +513,181 @@ const createAidMap = element => {
return true return true
} }
state.expandNativeCluster = feature => {
const source = state.map?.getSource(state.clusterSourceId)
const clusterId = Number(feature?.properties?.cluster_id)
if (!source || !Number.isFinite(clusterId)) return
Promise.resolve(source.getClusterExpansionZoom(clusterId)).then(expansionZoom => {
if (!state.active || !state.map) return
const currentZoom = state.map.getZoom()
const targetZoom = clusterExpansionTarget(currentZoom, Number(expansionZoom))
if (currentZoom >= 21.75 && targetZoom <= 22) {
const count = Number(
feature.properties?.total_count || feature.properties?.point_count
) || 1
const label = (state.element.dataset.clusterLabel || "%{count} items")
.replace("%{count}", new Intl.NumberFormat(uiLocale()).format(count))
new maplibregl.Popup({offset: 18})
.setLngLat(feature.geometry.coordinates)
.setDOMContent(state.popupContent({
title: label,
location: state.element.dataset.clusterMaxZoomLabel || ""
}))
.addTo(state.map)
return
}
state.clusterExpansionPending = true
state.map.easeTo({
center: feature.geometry.coordinates,
zoom: targetZoom,
duration: 300
})
})
}
state.expandServerCluster = feature => {
if (!feature || !state.map) return
const count = Number(feature.properties?.weight) || 1
const requestedZoom = Number(feature.properties?.expansion_zoom)
const currentZoom = state.map.getZoom()
const expansionZoom = clusterExpansionTarget(
currentZoom,
Number.isFinite(requestedZoom) ? requestedZoom : currentZoom + 2
)
if (currentZoom >= 21.75 && expansionZoom <= 22) {
const label = (state.element.dataset.clusterLabel || "%{count} items")
.replace("%{count}", String(count))
new maplibregl.Popup({offset: 18})
.setLngLat(feature.geometry.coordinates)
.setDOMContent(state.popupContent({
title: label,
location: state.element.dataset.clusterMaxZoomLabel || ""
}))
.addTo(state.map)
return
}
state.clusterExpansionPending = true
state.map.easeTo({
center: feature.geometry.coordinates,
zoom: expansionZoom,
duration: 300
})
}
state.syncClusterMarkers = () => {
state.markers.forEach(marker => marker.remove())
state.markers = []
if (
!state.map ||
state.element.dataset.discoveryMap !== "true" ||
!state.map.getLayer(state.clusterCircleId) ||
!state.map.getLayer(state.serverClusterCircleId)
) return
const features = state.map.queryRenderedFeatures({
layers: [state.clusterCircleId, state.serverClusterCircleId]
})
const seen = new Set()
features.forEach(feature => {
const nativeCluster =
feature.properties?.cluster === true || feature.properties?.cluster === "true"
const key = nativeCluster
? `native:${feature.properties.cluster_id}`
: `server:${feature.properties?.server_cluster_id || feature.properties?.id}`
if (seen.has(key)) return
seen.add(key)
const count = Number(
nativeCluster
? feature.properties?.total_count || feature.properties?.point_count
: feature.properties?.weight
) || 1
const urgentCount = Number(
nativeCluster
? feature.properties?.urgent_count
: feature.properties?.urgent_weight
) || 0
const button = document.createElement("button")
button.type = "button"
button.className = `request-map-cluster ${clusterSizeClass(count)}`
button.dataset.clusterCount = String(count)
button.classList.toggle("is-urgent", urgentCount > 0)
button.textContent = clusterCountLabel(count, uiLocale())
const formattedCount = new Intl.NumberFormat(uiLocale()).format(count)
button.setAttribute(
"aria-label",
(state.element.dataset.clusterLabel || "%{count} items")
.replace("%{count}", formattedCount)
)
button.title = button.getAttribute("aria-label")
button.addEventListener("click", event => {
event.stopPropagation()
if (nativeCluster) {
state.expandNativeCluster(feature)
} else {
state.expandServerCluster(feature)
}
})
state.markers.push(
new maplibregl.Marker({element: button})
.setLngLat(feature.geometry.coordinates)
.addTo(state.map)
)
})
}
state.syncVisibleAreas = () => {
if (!state.map) return
const areaSource = state.map.getSource(state.areaSourceId)
if (!areaSource) return
let visiblePoints = state.points
if (state.element.dataset.discoveryMap === "true") {
if (!state.map.getSource(state.clusterSourceId)?.loaded()) return
const visibleIds = new Set(
state.map
.querySourceFeatures(state.clusterSourceId, {
filter: [
"all",
["!", ["has", "point_count"]],
["!=", ["get", "item_type"], "cluster"]
]
})
.map(feature => feature.properties?.id)
.filter(Boolean)
)
visiblePoints = state.points.filter(point => visibleIds.has(point.id))
}
const signature = visiblePoints
.filter(point => !point.exact && Number.isFinite(point.radius_meters))
.map(point => point.id)
.sort()
.join(":")
if (signature === state.visibleAreaSignature) return
state.visibleAreaSignature = signature
areaSource.setData(areaFeatureCollection(visiblePoints))
}
state.renderMarkers = () => { state.renderMarkers = () => {
if (!state.map) return if (!state.map) return
@ -525,22 +696,45 @@ const createAidMap = element => {
state.markerElements.clear() state.markerElements.clear()
const points = markerPoints(state.element) const points = markerPoints(state.element)
const displayPoints = mergeNearbyMapPoints(state.map, points) state.points = points
const exactPoints = displayPoints.filter( const pointFeatures = pointFeatureCollection(points)
point => point.type !== "cluster" && (point.exact || !Number.isFinite(point.radius_meters)) const areaFeatures =
) state.element.dataset.discoveryMap === "true"
const clusterPoints = displayPoints.filter(point => point.type === "cluster") ? emptyFeatureCollection()
const areaFeatures = areaFeatureCollection(displayPoints) : areaFeatureCollection(points)
const clusterFeatures = clusterFeatureCollection(clusterPoints)
const bounds = new maplibregl.LngLatBounds() const bounds = new maplibregl.LngLatBounds()
exactPoints.forEach(point => bounds.extend([point.longitude, point.latitude])) points.forEach(point => {
areaFeatures.features.forEach(feature => extendBoundsWithFeature(bounds, feature)) const clusterBounds = point.bounds
clusterFeatures.features.forEach(feature => bounds.extend(feature.geometry.coordinates))
state.locationBounds = displayPoints.length > 0 ? bounds : null
if (state.map.isStyleLoaded()) { if (
const areaSource = state.map.getSource(state.areaSourceId) clusterBounds &&
[clusterBounds.west, clusterBounds.south, clusterBounds.east, clusterBounds.north]
.every(value => Number.isFinite(Number(value)))
) {
bounds.extend([Number(clusterBounds.west), Number(clusterBounds.south)])
bounds.extend([Number(clusterBounds.east), Number(clusterBounds.north)])
} else {
bounds.extend([point.longitude, point.latitude])
}
})
state.locationBounds = points.length > 0 ? bounds : null
const existingAreaSource = state.map.getSource(state.areaSourceId)
const existingPointSource = state.map.getSource(state.clusterSourceId)
if (
(!existingAreaSource || !existingPointSource) &&
!state.map.isStyleLoaded()
) {
state.pendingMarkerRender = true
return
}
state.pendingMarkerRender = false
{
const areaSource = existingAreaSource
if (areaSource) { if (areaSource) {
areaSource.setData(areaFeatures) areaSource.setData(areaFeatures)
@ -550,12 +744,14 @@ const createAidMap = element => {
id: state.areaFillId, id: state.areaFillId,
type: "fill", type: "fill",
source: state.areaSourceId, source: state.areaSourceId,
minzoom: state.element.dataset.discoveryMap === "true" ? 12 : 0,
paint: {"fill-color": "#278467", "fill-opacity": 0.18} paint: {"fill-color": "#278467", "fill-opacity": 0.18}
}) })
state.map.addLayer({ state.map.addLayer({
id: state.areaLineId, id: state.areaLineId,
type: "line", type: "line",
source: state.areaSourceId, source: state.areaSourceId,
minzoom: state.element.dataset.discoveryMap === "true" ? 12 : 0,
paint: { paint: {
"line-color": "#16725b", "line-color": "#16725b",
"line-width": 2, "line-width": 2,
@ -566,6 +762,7 @@ const createAidMap = element => {
id: state.areaSelectedId, id: state.areaSelectedId,
type: "line", type: "line",
source: state.areaSourceId, source: state.areaSourceId,
minzoom: state.element.dataset.discoveryMap === "true" ? 12 : 0,
filter: ["==", ["get", "id"], ""], filter: ["==", ["get", "id"], ""],
paint: {"line-color": "#d6573b", "line-width": 5} paint: {"line-color": "#d6573b", "line-width": 5}
}) })
@ -588,83 +785,171 @@ const createAidMap = element => {
}) })
} }
} const pointSource = existingPointSource
exactPoints.forEach(point => { if (pointSource) {
const popup = new maplibregl.Popup({offset: 18}).setDOMContent(state.popupContent(point)) pointSource.setData(pointFeatures)
const markerColor = point.type === "live" ? "#2563eb" : point.exact ? "#d6573b" : "#278467" } else {
const marker = new maplibregl.Marker({color: markerColor}) const sourceOptions = {
.setLngLat([point.longitude, point.latitude]) type: "geojson",
.setPopup(popup) data: pointFeatures,
.addTo(state.map) generateId: false
}
const markerElement = marker.getElement() if (state.element.dataset.discoveryMap === "true") {
markerElement.dataset.markerType = point.type || "request" Object.assign(sourceOptions, {
markerElement.setAttribute("aria-label", point.title || uiText("mapMarkerLabel")) maxzoom: 22,
cluster: true,
if (point.id) { clusterRadius: 64,
markerElement.dataset.requestId = point.id clusterMaxZoom: 21,
markerElement.addEventListener("click", () => state.selectRequest(point.id)) clusterMinPoints: 2,
state.markerElements.set(point.id, markerElement) clusterProperties: {
} total_count: ["+", ["get", "weight"]],
urgent_count: ["+", ["get", "urgent_weight"]]
state.markers.push(marker)
})
clusterPoints.forEach(point => {
const element = document.createElement("button")
element.type = "button"
element.className = `request-map-cluster ${clusterSizeClass(point.count)}`
element.dataset.clusterCount = String(point.count)
element.classList.toggle("is-urgent", Number(point.urgent_count) > 0)
element.textContent = String(point.count)
element.setAttribute(
"aria-label",
(state.element.dataset.clusterLabel || "%{count} requests")
.replace("%{count}", String(point.count))
)
element.title = element.getAttribute("aria-label")
element.addEventListener("click", event => {
event.stopPropagation()
state.clusterExpansionPending = true
const bounds = point.bounds
const hasBounds =
bounds &&
[bounds.west, bounds.south, bounds.east, bounds.north]
.every(value => Number.isFinite(Number(value)))
const hasArea =
hasBounds &&
(Math.abs(Number(bounds.east) - Number(bounds.west)) > 0.000001 ||
Math.abs(Number(bounds.north) - Number(bounds.south)) > 0.000001)
if (hasArea) {
state.map.fitBounds(
[
[Number(bounds.west), Number(bounds.south)],
[Number(bounds.east), Number(bounds.north)]
],
{
padding: 72,
maxZoom: Math.min(state.map.getZoom() + 5, 18),
duration: 300
} }
)
} else {
state.map.easeTo({
center: [point.longitude, point.latitude],
zoom: Math.min(state.map.getZoom() + 2, 20),
duration: 300
}) })
} }
})
const marker = new maplibregl.Marker({element}) state.map.addSource(state.clusterSourceId, sourceOptions)
.setLngLat([point.longitude, point.latitude]) state.map.addLayer({
.addTo(state.map) id: state.clusterCircleId,
type: "circle",
source: state.clusterSourceId,
filter: ["has", "point_count"],
paint: {
"circle-color": [
"case",
[">", ["coalesce", ["get", "urgent_count"], 0], 0],
"#c2412d",
"#087f5b"
],
"circle-radius": [
"step",
["coalesce", ["get", "total_count"], ["get", "point_count"]],
22,
100,
25,
1_000,
28,
10_000,
31,
100_000,
34
],
"circle-stroke-color": "#ffffff",
"circle-stroke-width": 3,
"circle-opacity": 0.96
}
})
state.map.addLayer({
id: state.serverClusterCircleId,
type: "circle",
source: state.clusterSourceId,
filter: [
"all",
["!", ["has", "point_count"]],
["==", ["get", "item_type"], "cluster"]
],
paint: {
"circle-color": [
"case",
[">", ["coalesce", ["get", "urgent_weight"], 0], 0],
"#c2412d",
"#087f5b"
],
"circle-radius": [
"step",
["get", "weight"],
22,
100,
25,
1_000,
28,
10_000,
31,
100_000,
34
],
"circle-stroke-color": "#ffffff",
"circle-stroke-width": 3,
"circle-opacity": 0.96
}
})
state.map.addLayer({
id: state.pointCircleId,
type: "circle",
source: state.clusterSourceId,
filter: [
"all",
["!", ["has", "point_count"]],
["!=", ["get", "item_type"], "cluster"]
],
paint: {
"circle-color": [
"case",
["==", ["get", "item_type"], "live"],
"#2563eb",
["==", ["get", "exact"], true],
"#d6573b",
"#278467"
],
"circle-radius": 9,
"circle-stroke-color": "#ffffff",
"circle-stroke-width": 3
}
})
state.map.addLayer({
id: state.pointSelectedId,
type: "circle",
source: state.clusterSourceId,
filter: ["==", ["get", "id"], state.selectedItemId || ""],
paint: {
"circle-color": "rgba(0,0,0,0)",
"circle-radius": 15,
"circle-stroke-color": "#111827",
"circle-stroke-width": 3
}
})
state.markers.push(marker) const interactiveLayers = [
}) state.clusterCircleId,
state.serverClusterCircleId,
state.pointCircleId
]
interactiveLayers.forEach(layerId => {
state.map.on("mouseenter", layerId, () => {
state.map.getCanvas().style.cursor = "pointer"
})
state.map.on("mouseleave", layerId, () => {
state.map.getCanvas().style.cursor = ""
})
})
state.map.on("click", state.clusterCircleId, event => {
const feature = event.features?.[0]
if (feature) state.expandNativeCluster(feature)
})
state.map.on("click", state.serverClusterCircleId, event => {
const feature = event.features?.[0]
if (feature) state.expandServerCluster(feature)
})
state.map.on("click", state.pointCircleId, event => {
const feature = event.features?.[0]
const properties = feature?.properties
if (!feature || !properties) return
if (properties.id) state.selectRequest(properties.id)
new maplibregl.Popup({offset: 18})
.setLngLat(feature.geometry.coordinates)
.setDOMContent(state.popupContent(properties))
.addTo(state.map)
})
}
}
if (state.element.dataset.autoFit !== "false") state.fitMarkers() if (state.element.dataset.autoFit !== "false") state.fitMarkers()
} }
@ -752,7 +1037,6 @@ const createAidMap = element => {
state.onMoveEnd = () => { state.onMoveEnd = () => {
if (!state.initializedViewport || state.element.dataset.discoveryMap !== "true") return if (!state.initializedViewport || state.element.dataset.discoveryMap !== "true") return
state.renderMarkers()
const viewport = mapViewport(state.map) const viewport = mapViewport(state.map)
if (!viewport) return if (!viewport) return
@ -764,11 +1048,23 @@ const createAidMap = element => {
} }
} }
state.onIdle = () => {
state.markReady()
if (state.pendingMarkerRender) {
state.renderMarkers()
if (state.pendingMarkerRender) return
}
state.syncVisibleAreas()
state.syncClusterMarkers()
}
state.onRequestHighlight = event => state.highlightRequest(event.detail?.id) state.onRequestHighlight = event => state.highlightRequest(event.detail?.id)
state.map.on("dataloading", state.markLoading) state.map.on("dataloading", state.markLoading)
state.map.on("load", state.onLoad) state.map.on("load", state.onLoad)
state.map.on("idle", state.markReady) state.map.on("idle", state.onIdle)
state.map.on("moveend", state.onMoveEnd) state.map.on("moveend", state.onMoveEnd)
window.addEventListener("wnh:request-highlight", state.onRequestHighlight) window.addEventListener("wnh:request-highlight", state.onRequestHighlight)
if ("ResizeObserver" in window) { if ("ResizeObserver" in window) {
@ -785,7 +1081,7 @@ const createAidMap = element => {
state.resizeObserver?.disconnect() state.resizeObserver?.disconnect()
state.map?.off("dataloading", state.markLoading) state.map?.off("dataloading", state.markLoading)
state.map?.off("load", state.onLoad) state.map?.off("load", state.onLoad)
state.map?.off("idle", state.markReady) state.map?.off("idle", state.onIdle)
state.map?.off("moveend", state.onMoveEnd) state.map?.off("moveend", state.onMoveEnd)
window.removeEventListener("wnh:request-highlight", state.onRequestHighlight) window.removeEventListener("wnh:request-highlight", state.onRequestHighlight)
state.map?.remove() state.map?.remove()

View File

@ -0,0 +1,59 @@
const finiteCount = (value, fallback) => {
const count = Number(value)
return Number.isFinite(count) && count >= 0 ? count : fallback
}
export const clusterSizeClass = count => {
if (count >= 100_000) return "is-xxl"
if (count >= 10_000) return "is-xl"
if (count >= 1_000) return "is-lg"
return "is-md"
}
export const clusterCountLabel = (count, locale = "en") => {
const value = finiteCount(count, 0)
if (value < 1_000) return new Intl.NumberFormat(locale).format(value)
return new Intl.NumberFormat(locale, {
notation: "compact",
maximumFractionDigits: value < 10_000 ? 1 : 0
}).format(value)
}
export const clusterExpansionTarget = (currentZoom, requestedZoom, maximumZoom = 22) => {
const current = finiteCount(currentZoom, 0)
const requested = finiteCount(requestedZoom, current + 1)
const nearestIntegerZoom = Math.round(current)
const normalizedCurrent =
Math.abs(current - nearestIntegerZoom) < 0.001 ? nearestIntegerZoom : current
const nextIntegerZoom = Math.floor(normalizedCurrent) + 1
return Math.min(maximumZoom, Math.max(nextIntegerZoom, requested))
}
export const pointFeatureCollection = points => ({
type: "FeatureCollection",
features: points.map(point => ({
type: "Feature",
id: point.id,
properties: {
id: point.id,
item_type: point.type || "request",
title: point.title || "",
location: point.location || "",
exact: point.exact === true,
radius_meters: Number.isFinite(point.radius_meters) ? point.radius_meters : 0,
weight: finiteCount(point.count, 1),
urgent_weight: finiteCount(point.urgent_count, 0),
server_cluster_id: point.cluster_id || "",
parent_cluster_id: point.parent_cluster_id || "",
hierarchy_level: finiteCount(point.hierarchy_level, 0),
expansion_zoom: finiteCount(point.expansion_zoom, 22)
},
geometry: {
type: "Point",
coordinates: [Number(point.longitude), Number(point.latitude)]
}
}))
})

View File

@ -0,0 +1,123 @@
import assert from "node:assert/strict"
import test from "node:test"
import {Supercluster} from "@maplibre/geojson-vt"
import {
clusterCountLabel,
clusterExpansionTarget,
clusterSizeClass,
pointFeatureCollection
} from "./map_cluster_features.mjs"
const buildIndex = points => {
const index = new Supercluster({
radius: 64,
maxZoom: 21,
minPoints: 2,
map: properties => ({
total_count: properties.weight,
urgent_count: properties.urgent_weight
}),
reduce: (accumulated, properties) => {
accumulated.total_count += properties.total_count
accumulated.urgent_count += properties.urgent_count
}
})
index.load(pointFeatureCollection(points).features)
return index
}
test("weighted server nodes preserve the represented record total", () => {
const index = buildIndex([
{id: "request:4:1:1", type: "cluster", longitude: 30.50, latitude: 50.45, count: 700},
{
id: "request:4:1:2",
type: "cluster",
longitude: 30.51,
latitude: 50.46,
count: 300,
urgent_count: 10
},
{id: "one", type: "request", longitude: -74.0, latitude: 40.7}
])
const world = index.getClusters([-180, -85, 180, 85], 0)
const represented = world.reduce(
(sum, feature) => sum + (feature.properties.total_count || feature.properties.weight),
0
)
const urgent = world.reduce(
(sum, feature) => sum + (feature.properties.urgent_count || feature.properties.urgent_weight),
0
)
assert.equal(represented, 1_001)
assert.equal(urgent, 10)
})
test("server hierarchy metadata survives GeoJSON conversion without becoming a native cluster", () => {
const feature = pointFeatureCollection([
{
id: "request:11:1197:1378",
type: "cluster",
cluster_id: "request:11:1197:1378",
parent_cluster_id: "request:10:598:689",
hierarchy_level: 11,
expansion_zoom: 13,
longitude: 30.52,
latitude: 50.45,
count: 27
}
]).features[0]
assert.equal(feature.properties.server_cluster_id, "request:11:1197:1378")
assert.equal(feature.properties.parent_cluster_id, "request:10:598:689")
assert.equal(feature.properties.hierarchy_level, 11)
assert.equal(feature.properties.expansion_zoom, 13)
assert.equal(feature.properties.weight, 27)
assert.equal("point_count" in feature.properties, false)
})
test("native clusters expose an expansion zoom and eventually split unique coordinates", () => {
const index = buildIndex([
{id: "a", longitude: 30.52340, latitude: 50.45010},
{id: "b", longitude: 30.52341, latitude: 50.45011},
{id: "c", longitude: 30.60, latitude: 50.50}
])
const world = index.getClusters([-180, -85, 180, 85], 0)
const cluster = world.find(feature => feature.properties.cluster)
assert.ok(cluster)
assert.ok(index.getClusterExpansionZoom(cluster.properties.cluster_id) > 0)
assert.equal(
index.getClusters([30.5, 50.4, 30.7, 50.6], 22)
.filter(feature => !feature.properties.cluster)
.length,
3
)
})
test("cluster size classes remain stable for accessible count buttons", () => {
assert.equal(clusterSizeClass(999), "is-md")
assert.equal(clusterSizeClass(1_000), "is-lg")
assert.equal(clusterSizeClass(10_000), "is-xl")
assert.equal(clusterSizeClass(100_000), "is-xxl")
})
test("large visual counts are compact while exact values remain available to accessibility text", () => {
assert.equal(clusterCountLabel(999, "en"), "999")
assert.equal(clusterCountLabel(1_250, "en"), "1.3K")
assert.equal(clusterCountLabel(48_533, "en"), "49K")
assert.equal(clusterCountLabel(1_000_000, "en"), "1M")
})
test("cluster expansion always advances at least one integer zoom level", () => {
assert.equal(clusterExpansionTarget(9, 9), 10)
assert.equal(clusterExpansionTarget(8.999_999_096_835_52, 9), 10)
assert.equal(clusterExpansionTarget(10.999_994_193_019_71, 11), 12)
assert.equal(clusterExpansionTarget(9.6, 10), 10)
assert.equal(clusterExpansionTarget(9.6, 13), 13)
assert.equal(clusterExpansionTarget(21.9, 24), 22)
})

View File

@ -1,147 +0,0 @@
const clusterCount = point => {
const count = Number(point?.count)
return Number.isFinite(count) && count > 0 ? count : 1
}
const urgentCount = point => {
const count = Number(point?.urgent_count)
return Number.isFinite(count) && count > 0 ? count : 0
}
export const clusterDiameterPixels = count => {
if (count >= 100_000) return 60
if (count >= 10_000) return 56
if (count >= 1_000) return 52
return 44
}
export const clusterSizeClass = count => {
if (count >= 100_000) return "is-xxl"
if (count >= 10_000) return "is-xl"
if (count >= 1_000) return "is-lg"
return "is-md"
}
const mergeDistance = (left, right, gap) =>
(clusterDiameterPixels(left.count) + clusterDiameterPixels(right.count)) / 2 + gap
const coordinatesFromPixel = (map, pixel) => {
const coordinates = map.unproject([pixel.x, pixel.y])
return {
longitude: Number(coordinates.lng ?? coordinates[0]),
latitude: Number(coordinates.lat ?? coordinates[1])
}
}
const finiteCoordinate = value => {
const coordinate = Number(value)
return Number.isFinite(coordinate) ? coordinate : null
}
const pointBounds = point => {
const longitude = finiteCoordinate(point?.longitude)
const latitude = finiteCoordinate(point?.latitude)
const source = point?.bounds || {}
const west = finiteCoordinate(source.west) ?? longitude
const south = finiteCoordinate(source.south) ?? latitude
const east = finiteCoordinate(source.east) ?? longitude
const north = finiteCoordinate(source.north) ?? latitude
if ([west, south, east, north].some(value => value === null)) return null
return {
west: Math.min(west, east),
south: Math.min(south, north),
east: Math.max(west, east),
north: Math.max(south, north)
}
}
const groupBounds = points => {
const bounds = points.map(pointBounds).filter(Boolean)
if (bounds.length === 0) return null
return bounds.reduce(
(result, current) => ({
west: Math.min(result.west, current.west),
south: Math.min(result.south, current.south),
east: Math.max(result.east, current.east),
north: Math.max(result.north, current.north)
}),
bounds[0]
)
}
const nearbyMapPointGroups = (map, points, options = {}) => {
const gap = Number.isFinite(options.gap) ? Math.max(options.gap, 0) : 8
const groups = points.map(point => {
const pixel = map.project([point.longitude, point.latitude])
return {
points: [point],
x: pixel.x,
y: pixel.y,
count: clusterCount(point),
urgent_count: urgentCount(point)
}
})
let merged = true
while (merged) {
merged = false
outer:
for (let leftIndex = 0; leftIndex < groups.length; leftIndex += 1) {
for (let rightIndex = leftIndex + 1; rightIndex < groups.length; rightIndex += 1) {
const left = groups[leftIndex]
const right = groups[rightIndex]
const distance = Math.hypot(left.x - right.x, left.y - right.y)
if (distance >= mergeDistance(left, right, gap)) continue
const count = left.count + right.count
left.x = (left.x * left.count + right.x * right.count) / count
left.y = (left.y * left.count + right.y * right.count) / count
left.count = count
left.urgent_count += right.urgent_count
left.points.push(...right.points)
groups.splice(rightIndex, 1)
merged = true
break outer
}
}
}
return groups
}
const visualCluster = (map, group) => {
const coordinates = coordinatesFromPixel(map, group)
const firstId = group.points[0]?.id || "anonymous"
return {
type: "cluster",
id: `visual-cluster-${group.points.length}-${firstId}`,
count: group.count,
urgent_count: group.urgent_count,
longitude: coordinates.longitude,
latitude: coordinates.latitude,
bounds: groupBounds(group.points)
}
}
export const mergeNearbyMapClusters = (map, points, options = {}) =>
nearbyMapPointGroups(map, points, options).map(group => visualCluster(map, group))
export const mergeNearbyMapPoints = (map, points, options = {}) => {
return nearbyMapPointGroups(map, points, options).map(group => {
const point = group.points[0]
if (group.points.length === 1 && point?.type !== "cluster") return point
return visualCluster(map, group)
})
}

View File

@ -1,101 +0,0 @@
import assert from "node:assert/strict"
import test from "node:test"
import {
clusterDiameterPixels,
clusterSizeClass,
mergeNearbyMapClusters,
mergeNearbyMapPoints
} from "./map_cluster_points.mjs"
const flatMap = scale => ({
project: ([longitude, latitude]) => ({x: longitude * scale, y: latitude * scale}),
unproject: ([x, y]) => ({lng: x / scale, lat: y / scale})
})
test("merges visually overlapping clusters and preserves their total", () => {
const points = [
{
id: "a",
type: "cluster",
count: 100,
urgent_count: 2,
longitude: 0,
latitude: 0,
bounds: {west: -5, south: -2, east: 5, north: 2}
},
{
id: "b",
type: "cluster",
count: 50,
urgent_count: 1,
longitude: 30,
latitude: 0,
bounds: {west: 25, south: -3, east: 35, north: 4}
},
{id: "c", type: "cluster", count: 7, urgent_count: 0, longitude: 140, latitude: 0}
]
const result = mergeNearbyMapClusters(flatMap(1), points)
assert.equal(result.length, 2)
assert.equal(result.reduce((total, cluster) => total + cluster.count, 0), 157)
const merged = result.find(cluster => cluster.count === 150)
assert.equal(merged.urgent_count, 3)
assert.equal(merged.longitude, 10)
assert.equal(merged.latitude, 0)
assert.deepEqual(merged.bounds, {west: -5, south: -3, east: 35, north: 4})
})
test("splits the same clusters again at a closer zoom", () => {
const points = [
{id: "a", type: "cluster", count: 20, longitude: 0, latitude: 0},
{id: "b", type: "cluster", count: 30, longitude: 30, latitude: 0}
]
assert.equal(mergeNearbyMapClusters(flatMap(1), points).length, 1)
assert.equal(mergeNearbyMapClusters(flatMap(3), points).length, 2)
})
test("merges a singleton into an overlapping cluster at a distant zoom", () => {
const points = [
{id: "cluster", type: "cluster", count: 351, longitude: 0, latitude: 0},
{id: "request", type: "request", longitude: 28, latitude: 0, exact: true}
]
const result = mergeNearbyMapPoints(flatMap(1), points)
assert.equal(result.length, 1)
assert.equal(result[0].type, "cluster")
assert.equal(result[0].count, 352)
assert.deepEqual(result[0].bounds, {west: 0, south: 0, east: 28, north: 0})
})
test("keeps separated singleton points interactive at a closer zoom", () => {
const points = [
{id: "left", type: "request", longitude: 0, latitude: 0, exact: true},
{
id: "right",
type: "request",
longitude: 30,
latitude: 0,
radius_meters: 1_000
}
]
const result = mergeNearbyMapPoints(flatMap(3), points)
assert.deepEqual(result, points)
})
test("uses larger non-overlapping markers for larger totals", () => {
assert.equal(clusterDiameterPixels(999), 44)
assert.equal(clusterDiameterPixels(1_000), 52)
assert.equal(clusterDiameterPixels(10_000), 56)
assert.equal(clusterDiameterPixels(100_000), 60)
assert.equal(clusterSizeClass(999), "is-md")
assert.equal(clusterSizeClass(1_000), "is-lg")
assert.equal(clusterSizeClass(10_000), "is-xl")
assert.equal(clusterSizeClass(100_000), "is-xxl")
})

View File

@ -11,7 +11,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
@default_rows 1_000_000 @default_rows 1_000_000
@synthetic_users 1_000 @synthetic_users 1_000
@viewer_email "scale-viewer@example.invalid" @viewer_email "scale-viewer@example.invalid"
@fixture_version 7 @fixture_version 8
@request_open_modulus 1 @request_open_modulus 1
@activity_open_modulus 1 @activity_open_modulus 1
@places [ @places [
@ -425,10 +425,11 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
'Synthetic data only; no real-world action is requested.', 'Synthetic data only; no real-world action is requested.',
jsonb_build_object( jsonb_build_object(
'scale_fixture', true, 'scale_fixture', true,
'scale_fixture_version', $7::integer 'scale_fixture_version', $7::integer,
'scale_place', place.name,
'scale_location_index', local.local_index
), ),
place.name || ' synthetic area ' || place.name || ' synthetic location ' || (local.local_index + 1),
((variation.category_key + variation.time_key) % 100 + 1),
CASE WHEN variation.visibility_key % 20 = 0 THEN NULL CASE WHEN variation.visibility_key % 20 = 0 THEN NULL
ELSE ST_SetSRID( ELSE ST_SetSRID(
ST_MakePoint(coordinates.longitude, coordinates.latitude), ST_MakePoint(coordinates.longitude, coordinates.latitude),
@ -469,8 +470,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
FROM generate_series(1, $1) AS value FROM generate_series(1, $1) AS value
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
md5('wnh-scale-request-v5-' || value) AS fixture_hash, md5('wnh-scale-request-v8-' || value) AS fixture_hash
md5('wnh-scale-request-geography-v5-' || value) AS geography_hash
) AS fixture ) AS fixture
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
@ -482,52 +482,56 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
AS time_key, AS time_key,
('x' || substr(fixture.fixture_hash, 25, 8))::bit(32)::bigint ('x' || substr(fixture.fixture_hash, 25, 8))::bit(32)::bigint
AS category_key, AS category_key,
('x' || substr(fixture.geography_hash, 1, 8))::bit(32)::bigint ('x' || substr(md5('wnh-scale-request-owner-v8-' || value), 1, 8))
AS place_key, ::bit(32)::bigint AS owner_key
('x' || substr(fixture.geography_hash, 9, 8))::bit(32)::bigint
AS angle_key,
('x' || substr(fixture.geography_hash, 17, 8))::bit(32)::bigint
AS distance_key,
('x' || substr(fixture.geography_hash, 25, 8))::bit(32)::bigint
AS owner_key
) AS variation ) AS variation
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT name, longitude, latitude SELECT name, longitude, latitude, position
FROM unnest( FROM unnest(
$4::text[], $4::text[],
$5::double precision[], $5::double precision[],
$6::double precision[] $6::double precision[]
) WITH ORDINALITY AS places(name, longitude, latitude, position) ) WITH ORDINALITY AS places(name, longitude, latitude, position)
WHERE places.position = variation.place_key % cardinality($4::text[]) + 1 WHERE places.position = ((value - 1) % cardinality($4::text[])) + 1
) AS place ) AS place
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
2 * pi() * (value - 1) / cardinality($4::text[]) AS local_index
((variation.angle_key % 1000003)::double precision / 1000003.0) AS angle, ) AS local
sqrt(
((variation.distance_key / 20 % 1000003)::double precision + 1) /
1000004.0
) *
CASE WHEN variation.distance_key % 20 < 12 THEN 15.0
WHEN variation.distance_key % 20 < 18 THEN 60.0
ELSE 220.0
END AS distance_km
) AS geography
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
place.latitude + (
geography.distance_km / 111.32 * sin(geography.angle) AS raw_latitude, ((local.local_index % 256) * 73 +
((local.local_index / 256) % 256) * 16) % 256
)::double precision AS grid_x,
(
(((local.local_index / 256) % 256) * 53 +
(local.local_index % 256) * 29) % 256
)::double precision AS grid_y
) AS grid
CROSS JOIN LATERAL (
SELECT
(grid.grid_x / 255.0 - 0.5) * 12.0 AS east_km,
(grid.grid_y / 255.0 - 0.5) * 12.0 AS north_km,
radians((place.position * 137.50776405) % 360.0) AS rotation
) AS local_offset
CROSS JOIN LATERAL (
SELECT
local_offset.east_km * cos(local_offset.rotation) -
local_offset.north_km * sin(local_offset.rotation) AS east_km,
local_offset.east_km * sin(local_offset.rotation) +
local_offset.north_km * cos(local_offset.rotation) AS north_km
) AS rotated_offset
CROSS JOIN LATERAL (
SELECT
greatest(
-84.0,
least(84.0, place.latitude + rotated_offset.north_km / 111.32)
) AS latitude,
place.longitude + place.longitude +
geography.distance_km / rotated_offset.east_km /
(111.32 * greatest(abs(cos(radians(place.latitude))), 0.2)) * (111.32 * greatest(abs(cos(radians(place.latitude))), 0.2))
cos(geography.angle) AS raw_longitude AS longitude
) AS raw_coordinates
CROSS JOIN LATERAL (
SELECT
greatest(-84.0, least(84.0, raw_coordinates.raw_latitude)) AS latitude,
(raw_coordinates.raw_longitude + 180.0) -
360.0 * floor((raw_coordinates.raw_longitude + 180.0) / 360.0) -
180.0 AS longitude
) AS coordinates ) AS coordinates
ON CONFLICT (id) DO UPDATE SET ON CONFLICT (id) DO UPDATE SET
title = EXCLUDED.title, title = EXCLUDED.title,
@ -578,10 +582,11 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
'Synthetic scale activity used to test geographic discovery, privacy, and UI.', 'Synthetic scale activity used to test geographic discovery, privacy, and UI.',
jsonb_build_object( jsonb_build_object(
'scale_fixture', true, 'scale_fixture', true,
'scale_fixture_version', $7::integer 'scale_fixture_version', $7::integer,
'scale_place', place.name,
'scale_location_index', local.local_index
), ),
place.name || ' synthetic area ' || place.name || ' synthetic location ' || (local.local_index + 1),
((variation.category_key + variation.time_key) % 100 + 1),
ST_SetSRID( ST_SetSRID(
ST_MakePoint(coordinates.longitude, coordinates.latitude), ST_MakePoint(coordinates.longitude, coordinates.latitude),
4326 4326
@ -615,8 +620,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
FROM generate_series(1, $1) AS value FROM generate_series(1, $1) AS value
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
md5('wnh-scale-activity-v5-' || value) AS fixture_hash, md5('wnh-scale-activity-v8-' || value) AS fixture_hash
md5('wnh-scale-activity-geography-v5-' || value) AS geography_hash
) AS fixture ) AS fixture
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
@ -628,52 +632,56 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
AS time_key, AS time_key,
('x' || substr(fixture.fixture_hash, 25, 8))::bit(32)::bigint ('x' || substr(fixture.fixture_hash, 25, 8))::bit(32)::bigint
AS category_key, AS category_key,
('x' || substr(fixture.geography_hash, 1, 8))::bit(32)::bigint ('x' || substr(md5('wnh-scale-activity-owner-v8-' || value), 1, 8))
AS place_key, ::bit(32)::bigint AS owner_key
('x' || substr(fixture.geography_hash, 9, 8))::bit(32)::bigint
AS angle_key,
('x' || substr(fixture.geography_hash, 17, 8))::bit(32)::bigint
AS distance_key,
('x' || substr(fixture.geography_hash, 25, 8))::bit(32)::bigint
AS owner_key
) AS variation ) AS variation
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT name, longitude, latitude SELECT name, longitude, latitude, position
FROM unnest( FROM unnest(
$4::text[], $4::text[],
$5::double precision[], $5::double precision[],
$6::double precision[] $6::double precision[]
) WITH ORDINALITY AS places(name, longitude, latitude, position) ) WITH ORDINALITY AS places(name, longitude, latitude, position)
WHERE places.position = variation.place_key % cardinality($4::text[]) + 1 WHERE places.position = ((value - 1) % cardinality($4::text[])) + 1
) AS place ) AS place
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
2 * pi() * (value - 1) / cardinality($4::text[]) AS local_index
((variation.angle_key % 1000003)::double precision / 1000003.0) AS angle, ) AS local
sqrt(
((variation.distance_key / 20 % 1000003)::double precision + 1) /
1000004.0
) *
CASE WHEN variation.distance_key % 20 < 12 THEN 15.0
WHEN variation.distance_key % 20 < 18 THEN 60.0
ELSE 220.0
END AS distance_km
) AS geography
CROSS JOIN LATERAL ( CROSS JOIN LATERAL (
SELECT SELECT
place.latitude + (
geography.distance_km / 111.32 * sin(geography.angle) AS raw_latitude, ((local.local_index % 256) * 73 +
((local.local_index / 256) % 256) * 16 + 31) % 256
)::double precision AS grid_x,
(
(((local.local_index / 256) % 256) * 53 +
(local.local_index % 256) * 29 + 47) % 256
)::double precision AS grid_y
) AS grid
CROSS JOIN LATERAL (
SELECT
(grid.grid_x / 255.0 - 0.5) * 12.0 AS east_km,
(grid.grid_y / 255.0 - 0.5) * 12.0 AS north_km,
radians((place.position * 137.50776405 + 19.0) % 360.0) AS rotation
) AS local_offset
CROSS JOIN LATERAL (
SELECT
local_offset.east_km * cos(local_offset.rotation) -
local_offset.north_km * sin(local_offset.rotation) AS east_km,
local_offset.east_km * sin(local_offset.rotation) +
local_offset.north_km * cos(local_offset.rotation) AS north_km
) AS rotated_offset
CROSS JOIN LATERAL (
SELECT
greatest(
-84.0,
least(84.0, place.latitude + rotated_offset.north_km / 111.32)
) AS latitude,
place.longitude + place.longitude +
geography.distance_km / rotated_offset.east_km /
(111.32 * greatest(abs(cos(radians(place.latitude))), 0.2)) * (111.32 * greatest(abs(cos(radians(place.latitude))), 0.2))
cos(geography.angle) AS raw_longitude AS longitude
) AS raw_coordinates
CROSS JOIN LATERAL (
SELECT
greatest(-84.0, least(84.0, raw_coordinates.raw_latitude)) AS latitude,
(raw_coordinates.raw_longitude + 180.0) -
360.0 * floor((raw_coordinates.raw_longitude + 180.0) / 360.0) -
180.0 AS longitude
) AS coordinates ) AS coordinates
ON CONFLICT (id) DO UPDATE SET ON CONFLICT (id) DO UPDATE SET
title = EXCLUDED.title, title = EXCLUDED.title,
@ -1125,7 +1133,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
defp fixture_place_count!("help_requests") do defp fixture_place_count!("help_requests") do
scalar!(""" scalar!("""
SELECT count(DISTINCT regexp_replace(location_label, ' synthetic area [0-9]+$', '')) SELECT count(DISTINCT regexp_replace(location_label, ' synthetic location [0-9]+$', ''))
FROM help_requests FROM help_requests
WHERE structured_data @> '{"scale_fixture": true}'::jsonb WHERE structured_data @> '{"scale_fixture": true}'::jsonb
""") """)
@ -1133,7 +1141,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
defp fixture_place_count!("activities") do defp fixture_place_count!("activities") do
scalar!(""" scalar!("""
SELECT count(DISTINCT regexp_replace(location_label, ' synthetic area [0-9]+$', '')) SELECT count(DISTINCT regexp_replace(location_label, ' synthetic location [0-9]+$', ''))
FROM activities FROM activities
WHERE structured_data @> '{"scale_fixture": true}'::jsonb WHERE structured_data @> '{"scale_fixture": true}'::jsonb
""") """)
@ -1146,7 +1154,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
SELECT count(*) AS place_rows SELECT count(*) AS place_rows
FROM help_requests FROM help_requests
WHERE structured_data @> '{"scale_fixture": true}'::jsonb WHERE structured_data @> '{"scale_fixture": true}'::jsonb
GROUP BY regexp_replace(location_label, ' synthetic area [0-9]+$', '') GROUP BY regexp_replace(location_label, ' synthetic location [0-9]+$', '')
) AS grouped_places ) AS grouped_places
""") """)
end end
@ -1158,7 +1166,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
SELECT count(*) AS place_rows SELECT count(*) AS place_rows
FROM activities FROM activities
WHERE structured_data @> '{"scale_fixture": true}'::jsonb WHERE structured_data @> '{"scale_fixture": true}'::jsonb
GROUP BY regexp_replace(location_label, ' synthetic area [0-9]+$', '') GROUP BY regexp_replace(location_label, ' synthetic location [0-9]+$', '')
) AS grouped_places ) AS grouped_places
""") """)
end end
@ -1173,7 +1181,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
AND status = 'open' AND status = 'open'
AND expires_at > now() AND expires_at > now()
AND location_visibility <> 'hidden' AND location_visibility <> 'hidden'
GROUP BY regexp_replace(location_label, ' synthetic area [0-9]+$', '') GROUP BY regexp_replace(location_label, ' synthetic location [0-9]+$', '')
) AS grouped_places ) AS grouped_places
""") """)
end end
@ -1188,7 +1196,7 @@ defmodule Mix.Tasks.Wnh.ScaleFixtures do
AND status = 'open' AND status = 'open'
AND starts_at > now() AND starts_at > now()
AND location_visibility = 'approximate_public' AND location_visibility = 'approximate_public'
GROUP BY regexp_replace(location_label, ' synthetic area [0-9]+$', '') GROUP BY regexp_replace(location_label, ' synthetic location [0-9]+$', '')
) AS grouped_places ) AS grouped_places
""") """)
end end

View File

@ -10,7 +10,7 @@ defmodule WhoNeedHelp.Activities do
alias WhoNeedHelp.Activities.{Activity, Message, Participant} alias WhoNeedHelp.Activities.{Activity, Message, Participant}
alias WhoNeedHelp.Catalog alias WhoNeedHelp.Catalog
alias WhoNeedHelp.Catalog.Category alias WhoNeedHelp.Catalog.Category
alias WhoNeedHelp.Help.DiscoveryViewport alias WhoNeedHelp.Help.{DiscoveryCluster, DiscoveryViewport}
alias WhoNeedHelp.Pagination alias WhoNeedHelp.Pagination
alias WhoNeedHelp.Repo alias WhoNeedHelp.Repo
alias WhoNeedHelp.Trust alias WhoNeedHelp.Trust
@ -53,7 +53,9 @@ defmodule WhoNeedHelp.Activities do
end end
def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do
cell_size = DiscoveryViewport.cluster_cell_meters(viewport) level = DiscoveryCluster.level_for_zoom(viewport.zoom)
cell_size = DiscoveryCluster.cell_size_meters(level)
world_half = DiscoveryCluster.world_half_meters()
points = points =
user user
@ -87,65 +89,75 @@ defmodule WhoNeedHelp.Activities do
points points
|> subquery() |> subquery()
|> group_by( |> group_by([point], [
[point],
fragment( fragment(
"ST_SnapToGrid(ST_Transform(?, 3857), ?)", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size)
),
fragment(
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
) ])
|> select([point], %{ |> select([point], %{
count: count(point.id), count: count(point.id),
activity_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id), activity_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id),
title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title), title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title),
location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label), location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label),
longitude: longitude: fragment("avg(ST_X(?))", point.public_location),
fragment( latitude: fragment("avg(ST_Y(?))", point.public_location),
"CASE WHEN count(*) = 1 THEN min(ST_X(?)) ELSE avg(ST_X(?)) END",
point.public_location,
point.public_location
),
latitude:
fragment(
"CASE WHEN count(*) = 1 THEN min(ST_Y(?)) ELSE avg(ST_Y(?)) END",
point.public_location,
point.public_location
),
west: fragment("min(ST_X(?))", point.public_location), west: fragment("min(ST_X(?))", point.public_location),
south: fragment("min(ST_Y(?))", point.public_location), south: fragment("min(ST_Y(?))", point.public_location),
east: fragment("max(ST_X(?))", point.public_location), east: fragment("max(ST_X(?))", point.public_location),
north: fragment("max(ST_Y(?))", point.public_location), north: fragment("max(ST_Y(?))", point.public_location),
west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location),
south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location),
east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location),
north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location),
cell_x: cell_x:
fragment( fragment(
"ST_X(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
), ),
cell_y: cell_y:
fragment( fragment(
"ST_Y(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
}) })
|> order_by( |> order_by([point],
[point],
asc: asc:
fragment( fragment(
"ST_X(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
), ),
asc: asc:
fragment( fragment(
"ST_Y(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
) )
|> Repo.all() |> Repo.all()
|> Enum.map(&map_discovery_item(&1, viewport.zoom)) |> Enum.map(&map_discovery_item(&1, level))
end
def count_map_discovery_items(%Scope{user: user}, filters) do
user
|> open_activities_query(filters, nil)
|> where([activity], activity.location_visibility == :approximate_public)
|> Repo.aggregate(:count, :id)
end end
def list_my_activities(%Scope{user: user}) do def list_my_activities(%Scope{user: user}) do
@ -968,10 +980,12 @@ defmodule WhoNeedHelp.Activities do
defp longitude_in_viewport?(longitude, %DiscoveryViewport{west: west, east: east}), defp longitude_in_viewport?(longitude, %DiscoveryViewport{west: west, east: east}),
do: longitude >= west or longitude <= east do: longitude >= west or longitude <= east
defp map_discovery_item(%{count: 1} = row, _zoom) do defp map_discovery_item(%{count: 1} = row, level) do
%{ %{
type: "activity", type: "activity",
id: row.activity_id, id: row.activity_id,
parent_cluster_id: DiscoveryCluster.parent_id(:activity, level, row.cell_x, row.cell_y),
hierarchy_level: level,
title: row.title, title: row.title,
location: row.location_label, location: row.location_label,
latitude: row.latitude, latitude: row.latitude,
@ -980,15 +994,24 @@ defmodule WhoNeedHelp.Activities do
} }
end end
defp map_discovery_item(row, zoom) do defp map_discovery_item(row, level) do
digest = cluster_id = DiscoveryCluster.cluster_id(:activity, level, row.cell_x, row.cell_y)
:crypto.hash(:sha256, "activity:#{zoom}:#{row.cell_x}:#{row.cell_y}")
|> Base.url_encode64(padding: false)
|> binary_part(0, 16)
%{ %{
type: "cluster", type: "cluster",
id: "activity-cluster-#{digest}", id: cluster_id,
cluster_id: cluster_id,
parent_cluster_id: DiscoveryCluster.parent_id(:activity, level, row.cell_x, row.cell_y),
hierarchy_level: level,
point_count: row.count,
expansion_zoom:
DiscoveryCluster.expansion_zoom(
level,
row.west_m,
row.south_m,
row.east_m,
row.north_m
),
count: row.count, count: row.count,
latitude: row.latitude, latitude: row.latitude,
longitude: row.longitude, longitude: row.longitude,

View File

@ -7,7 +7,7 @@ defmodule WhoNeedHelp.Help do
alias WhoNeedHelp.Accounts.Scope alias WhoNeedHelp.Accounts.Scope
alias WhoNeedHelp.Catalog alias WhoNeedHelp.Catalog
alias WhoNeedHelp.Catalog.Category alias WhoNeedHelp.Catalog.Category
alias WhoNeedHelp.Help.{Assignment, DiscoveryViewport, HelpRequest} alias WhoNeedHelp.Help.{Assignment, DiscoveryCluster, DiscoveryViewport, HelpRequest}
alias WhoNeedHelp.Pagination alias WhoNeedHelp.Pagination
alias WhoNeedHelp.ProductAnalytics alias WhoNeedHelp.ProductAnalytics
alias WhoNeedHelp.Push alias WhoNeedHelp.Push
@ -64,12 +64,17 @@ defmodule WhoNeedHelp.Help do
end end
def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do
cell_size = DiscoveryViewport.cluster_cell_meters(viewport) level = DiscoveryCluster.level_for_zoom(viewport.zoom)
cell_size = DiscoveryCluster.cell_size_meters(level)
world_half = DiscoveryCluster.world_half_meters()
points = points =
user user
|> open_requests_query(filters, nil) |> open_requests_query(filters, nil)
|> where([request], not is_nil(request.location)) |> where(
[request],
not is_nil(request.location) and request.location_visibility != :hidden
)
|> filter_request_map_viewport(viewport) |> filter_request_map_viewport(viewport)
|> select([request], %{ |> select([request], %{
id: request.id, id: request.id,
@ -105,14 +110,20 @@ defmodule WhoNeedHelp.Help do
points points
|> subquery() |> subquery()
|> group_by( |> group_by([point], [
[point],
fragment( fragment(
"ST_SnapToGrid(ST_Transform(?, 3857), ?)", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size)
),
fragment(
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
) ])
|> select([point], %{ |> select([point], %{
count: count(point.id), count: count(point.id),
urgent_count: fragment("count(*) FILTER (WHERE ? = 'now')::bigint", point.urgency), urgent_count: fragment("count(*) FILTER (WHERE ? = 'now')::bigint", point.urgency),
@ -125,52 +136,59 @@ defmodule WhoNeedHelp.Help do
point.location_visibility point.location_visibility
), ),
radius_meters: fragment("CASE WHEN count(*) = 1 THEN max(?) END", point.radius_meters), radius_meters: fragment("CASE WHEN count(*) = 1 THEN max(?) END", point.radius_meters),
longitude: longitude: fragment("avg(ST_X(?))", point.public_location),
fragment( latitude: fragment("avg(ST_Y(?))", point.public_location),
"CASE WHEN count(*) = 1 THEN min(ST_X(?)) ELSE avg(ST_X(?)) END",
point.public_location,
point.public_location
),
latitude:
fragment(
"CASE WHEN count(*) = 1 THEN min(ST_Y(?)) ELSE avg(ST_Y(?)) END",
point.public_location,
point.public_location
),
west: fragment("min(ST_X(?))", point.public_location), west: fragment("min(ST_X(?))", point.public_location),
south: fragment("min(ST_Y(?))", point.public_location), south: fragment("min(ST_Y(?))", point.public_location),
east: fragment("max(ST_X(?))", point.public_location), east: fragment("max(ST_X(?))", point.public_location),
north: fragment("max(ST_Y(?))", point.public_location), north: fragment("max(ST_Y(?))", point.public_location),
west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location),
south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location),
east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location),
north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location),
cell_x: cell_x:
fragment( fragment(
"ST_X(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
), ),
cell_y: cell_y:
fragment( fragment(
"ST_Y(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
}) })
|> order_by( |> order_by([point],
[point],
asc: asc:
fragment( fragment(
"ST_X(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
), ),
asc: asc:
fragment( fragment(
"ST_Y(ST_SnapToGrid(ST_Transform(?, 3857), ?))", "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
point.public_location, point.public_location,
constant(^world_half),
constant(^cell_size) constant(^cell_size)
) )
) )
|> Repo.all() |> Repo.all()
|> Enum.map(&map_discovery_item(&1, viewport.zoom)) |> Enum.map(&map_discovery_item(&1, level))
end
def count_map_discovery_items(%Scope{user: user}, filters) do
user
|> open_requests_query(filters, nil)
|> where(
[request],
not is_nil(request.location) and request.location_visibility != :hidden
)
|> Repo.aggregate(:count, :id)
end end
# Discovery lists include a privacy area's intersection with the viewport. # Discovery lists include a privacy area's intersection with the viewport.
@ -1274,10 +1292,12 @@ defmodule WhoNeedHelp.Help do
where(query, ^condition) where(query, ^condition)
end end
defp map_discovery_item(%{count: 1} = row, _zoom) do defp map_discovery_item(%{count: 1} = row, level) do
%{ %{
type: "request", type: "request",
id: row.request_id, id: row.request_id,
parent_cluster_id: DiscoveryCluster.parent_id(:request, level, row.cell_x, row.cell_y),
hierarchy_level: level,
title: row.title, title: row.title,
location: row.location_label, location: row.location_label,
latitude: row.latitude, latitude: row.latitude,
@ -1287,15 +1307,24 @@ defmodule WhoNeedHelp.Help do
} }
end end
defp map_discovery_item(row, zoom) do defp map_discovery_item(row, level) do
digest = cluster_id = DiscoveryCluster.cluster_id(:request, level, row.cell_x, row.cell_y)
:crypto.hash(:sha256, "#{zoom}:#{row.cell_x}:#{row.cell_y}")
|> Base.url_encode64(padding: false)
|> binary_part(0, 16)
%{ %{
type: "cluster", type: "cluster",
id: "cluster-#{digest}", id: cluster_id,
cluster_id: cluster_id,
parent_cluster_id: DiscoveryCluster.parent_id(:request, level, row.cell_x, row.cell_y),
hierarchy_level: level,
point_count: row.count,
expansion_zoom:
DiscoveryCluster.expansion_zoom(
level,
row.west_m,
row.south_m,
row.east_m,
row.north_m
),
count: row.count, count: row.count,
urgent_count: row.urgent_count, urgent_count: row.urgent_count,
latitude: row.latitude, latitude: row.latitude,

View File

@ -0,0 +1,99 @@
defmodule WhoNeedHelp.Help.DiscoveryCluster do
@moduledoc """
Stable Web Mercator hierarchy used as the server-side input to MapLibre
Supercluster.
A browser cannot safely receive every discovery point once the data set
reaches millions of rows. The server therefore groups points into a fixed
quadtree. Unlike viewport-sized `ST_SnapToGrid` buckets, every cell has a
stable identity and every cell at level `n + 1` belongs to exactly one parent
at level `n`.
MapLibre receives these weighted nodes and applies its native Supercluster
implementation for pixel-perfect collision handling. When a server node is
reached, `expansion_zoom/5` identifies the first map zoom where that node has
more than one child cell.
"""
@world_half_meters 20_037_508.342_789_244
@world_width_meters @world_half_meters * 2.0
@cluster_level_offset 2
@minimum_level 0
@maximum_map_zoom 22
@maximum_level @maximum_map_zoom + @cluster_level_offset
@type kind :: :request | :activity
def level_for_zoom(zoom) when is_number(zoom) do
zoom
|> floor()
|> Kernel.+(@cluster_level_offset)
|> clamp(@minimum_level, @maximum_level)
end
def cell_size_meters(level) when is_integer(level) and level in 0..@maximum_level do
@world_width_meters / :math.pow(2.0, level)
end
def world_half_meters, do: @world_half_meters
def cluster_id(kind, level, cell_x, cell_y)
when kind in [:request, :activity] and is_integer(level) and is_integer(cell_x) and
is_integer(cell_y) do
"#{kind}:#{level}:#{cell_x}:#{cell_y}"
end
def parent_id(_kind, 0, _cell_x, _cell_y), do: nil
def parent_id(kind, level, cell_x, cell_y)
when kind in [:request, :activity] and is_integer(level) and level > 0 and
is_integer(cell_x) and is_integer(cell_y) do
cluster_id(kind, level - 1, div(cell_x, 2), div(cell_y, 2))
end
@doc """
Returns the first map zoom at which a fixed hierarchy cell separates.
The bounds must be expressed in EPSG:3857 meters and describe the actual
member points, not the cell envelope. For coincident points the result is the
maximum supported map zoom.
"""
def expansion_zoom(level, west, south, east, north)
when is_integer(level) and is_number(west) and is_number(south) and is_number(east) and
is_number(north) do
west = clamp_coordinate(west)
east = clamp_coordinate(east)
south = clamp_coordinate(south)
north = clamp_coordinate(north)
candidate_level =
if level >= @maximum_level do
@maximum_level
else
Enum.find((level + 1)..@maximum_level, @maximum_level, fn candidate ->
cell_index(west, candidate) != cell_index(east, candidate) or
cell_index(south, candidate) != cell_index(north, candidate)
end)
end
candidate_level
|> Kernel.-(@cluster_level_offset)
|> clamp(0, @maximum_map_zoom)
end
defp cell_index(coordinate, level) do
coordinate
|> Kernel.+(@world_half_meters)
|> Kernel./(cell_size_meters(level))
|> floor()
end
defp clamp_coordinate(value) do
value
|> Kernel.*(1.0)
|> max(-@world_half_meters)
|> min(@world_half_meters - 1.0e-7)
end
defp clamp(value, minimum, maximum), do: value |> max(minimum) |> min(maximum)
end

View File

@ -2,18 +2,18 @@ defmodule WhoNeedHelp.Help.DiscoveryViewport do
@moduledoc """ @moduledoc """
Validated map bounds used by request discovery. Validated map bounds used by request discovery.
The viewport carries its rendered pixel size so server-side clustering can The viewport carries its rendered pixel size for subscription capacity and
match the amount of detail the browser can actually display. Longitudes input validation. Discovery clustering itself uses a stable zoom-indexed
crossing the antimeridian are represented as two PostGIS envelopes. hierarchy, so a cluster does not change identity when the canvas is resized.
Longitudes crossing the antimeridian are represented as two PostGIS
envelopes.
""" """
@max_mercator_latitude 85.051_128_78 @max_mercator_latitude 85.051_128_78
@maximum_canvas_dimension 4_096 @maximum_canvas_dimension 4_096
@minimum_canvas_dimension 160 @minimum_canvas_dimension 160
@cluster_cell_pixels 64.0
@map_tile_pixels 256 @map_tile_pixels 256
@maximum_subscription_zoom 12 @maximum_subscription_zoom 12
@earth_radius_meters 6_378_137.0
@enforce_keys [:west, :south, :east, :north, :zoom, :width, :height] @enforce_keys [:west, :south, :east, :north, :zoom, :width, :height]
defstruct [:west, :south, :east, :north, :zoom, :width, :height] defstruct [:west, :south, :east, :north, :zoom, :width, :height]
@ -106,22 +106,6 @@ defmodule WhoNeedHelp.Help.DiscoveryViewport do
|> Enum.uniq() |> Enum.uniq()
end end
def cluster_cell_meters(%__MODULE__{} = viewport) do
horizontal_cells = max(viewport.width / @cluster_cell_pixels, 1.0)
vertical_cells = max(viewport.height / @cluster_cell_pixels, 1.0)
width_meters =
viewport
|> envelopes()
|> Enum.map(fn {west, _south, east, _north} -> mercator_x(east) - mercator_x(west) end)
|> Enum.sum()
height_meters = mercator_y(viewport.north) - mercator_y(viewport.south)
max(width_meters / horizontal_cells, height_meters / vertical_cells)
|> max(0.01)
end
def query_params(%__MODULE__{} = viewport) do def query_params(%__MODULE__{} = viewport) do
%{ %{
"west" => format_number(viewport.west), "west" => format_number(viewport.west),
@ -310,14 +294,6 @@ defmodule WhoNeedHelp.Help.DiscoveryViewport do
defp latitude?(value), defp latitude?(value),
do: value >= -@max_mercator_latitude and value <= @max_mercator_latitude do: value >= -@max_mercator_latitude and value <= @max_mercator_latitude
defp mercator_x(longitude), do: @earth_radius_meters * degrees_to_radians(longitude)
defp mercator_y(latitude) do
latitude = min(max(latitude, -@max_mercator_latitude), @max_mercator_latitude)
radians = degrees_to_radians(latitude)
@earth_radius_meters * :math.log(:math.tan(:math.pi() / 4 + radians / 2))
end
defp degrees_to_radians(value), do: value * :math.pi() / 180.0 defp degrees_to_radians(value), do: value * :math.pi() / 180.0
defp clamp(value, minimum, maximum), do: min(max(value, minimum), maximum) defp clamp(value, minimum, maximum), do: min(max(value, minimum), maximum)

View File

@ -29,6 +29,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|> assign(:activities_cursor, nil) |> assign(:activities_cursor, nil)
|> assign(:map_items, Jason.encode!([])) |> assign(:map_items, Jason.encode!([]))
|> assign(:map_result_count, 0) |> assign(:map_result_count, 0)
|> assign(:map_total_count, 0)
|> assign(:discovery_loading, true) |> assign(:discovery_loading, true)
|> assign(:refresh_timer, nil) |> assign(:refresh_timer, nil)
|> assign(:my_activities, []) |> assign(:my_activities, [])
@ -266,6 +267,15 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
activities = activities_page.entries activities = activities_page.entries
map_total_count =
if discovery_mode == "nearby" and view_mode != "list" do
DiscoveryQueryCache.fetch(map_total_cache_key(scope, filters), fn ->
Activities.count_map_discovery_items(scope, filters)
end)
else
0
end
%{ %{
discovery_loading: false, discovery_loading: false,
activities: activities, activities: activities,
@ -273,7 +283,8 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
my_activities: my_activities_page.entries, my_activities: my_activities_page.entries,
my_activities_cursor: my_activities_page.next_cursor, my_activities_cursor: my_activities_page.next_cursor,
map_items: Jason.encode!(map_items), map_items: Jason.encode!(map_items),
map_result_count: Enum.reduce(map_items, 0, &(map_item_count(&1) + &2)) map_result_count: Enum.reduce(map_items, 0, &(map_item_count(&1) + &2)),
map_total_count: map_total_count
} }
end end
@ -338,6 +349,11 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
) )
) )
:ok =
DiscoveryQueryCache.invalidate(
map_total_cache_key(socket.assigns.current_scope, socket.assigns.filters)
)
socket socket
end end
@ -345,6 +361,8 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
{:activities, scope.user.id, filters, discovery_mode, view_mode, viewport} {:activities, scope.user.id, filters, discovery_mode, view_mode, viewport}
end end
defp map_total_cache_key(scope, filters), do: {:activity_map_total, scope.user.id, filters}
defp empty_filters, do: %{"category_id" => "", "area" => ""} defp empty_filters, do: %{"category_id" => "", "area" => ""}
defp normalize_filters(params) do defp normalize_filters(params) do
@ -513,6 +531,11 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
count: @map_result_count count: @map_result_count
)} )}
</strong> </strong>
<span class="ml-1 text-sm text-base-content/60">
{gettext("of %{count} matching activities with map locations",
count: @map_total_count
)}
</span>
<p class="text-sm text-base-content/60"> <p class="text-sm text-base-content/60">
{gettext("Move the map, then search the newly visible area.")} {gettext("Move the map, then search the newly visible area.")}
</p> </p>
@ -649,6 +672,9 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
data-item-path-prefix="/activities" data-item-path-prefix="/activities"
data-item-label={gettext("Open activity")} data-item-label={gettext("Open activity")}
data-cluster-label={gettext("%{count} activities", count: "%{count}")} data-cluster-label={gettext("%{count} activities", count: "%{count}")}
data-cluster-max-zoom-label={
gettext("Maximum zoom reached. Matching activities are listed beside the map.")
}
data-map-unavailable-label={ data-map-unavailable-label={
gettext("The map is unavailable in this browser. Activity details remain usable.") gettext("The map is unavailable in this browser. Activity details remain usable.")
} }

View File

@ -41,6 +41,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
|> assign(:reputation, @empty_reputation) |> assign(:reputation, @empty_reputation)
|> assign(:map_items, Jason.encode!([])) |> assign(:map_items, Jason.encode!([]))
|> assign(:map_result_count, 0) |> assign(:map_result_count, 0)
|> assign(:map_total_count, 0)
|> assign(:discovery_loading, true) |> assign(:discovery_loading, true)
|> assign(:refresh_timer, nil) |> assign(:refresh_timer, nil)
|> assign(:page_title, gettext("Nearby help")) |> assign(:page_title, gettext("Nearby help"))
@ -259,6 +260,15 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
%Pagination.Page{} %Pagination.Page{}
end end
map_total_count =
if discovery_mode == "nearby" and view_mode != "list" do
DiscoveryQueryCache.fetch(map_total_cache_key(scope, filters), fn ->
Help.count_map_discovery_items(scope, filters)
end)
else
0
end
%{ %{
discovery_loading: false, discovery_loading: false,
requests: requests_page.entries, requests: requests_page.entries,
@ -267,7 +277,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
my_requests_cursor: my_requests_page.next_cursor, my_requests_cursor: my_requests_page.next_cursor,
reputation: Trust.reputation(scope.user.id), reputation: Trust.reputation(scope.user.id),
map_items: Jason.encode!(map_items), map_items: Jason.encode!(map_items),
map_result_count: Enum.reduce(map_items, 0, &(map_item_count(&1) + &2)) map_result_count: Enum.reduce(map_items, 0, &(map_item_count(&1) + &2)),
map_total_count: map_total_count
} }
end end
@ -321,6 +332,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
) )
) )
:ok =
DiscoveryQueryCache.invalidate(
map_total_cache_key(socket.assigns.current_scope, socket.assigns.filters)
)
socket socket
end end
@ -328,6 +344,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
{:requests, scope.user.id, filters, discovery_mode, view_mode, viewport} {:requests, scope.user.id, filters, discovery_mode, view_mode, viewport}
end end
defp map_total_cache_key(scope, filters), do: {:request_map_total, scope.user.id, filters}
defp empty_filters, do: %{"category_id" => "", "urgency" => "", "area" => ""} defp empty_filters, do: %{"category_id" => "", "urgency" => "", "area" => ""}
defp normalize_filters(params) do defp normalize_filters(params) do
@ -525,6 +543,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
count: @map_result_count count: @map_result_count
)} )}
</strong> </strong>
<span class="ml-1 text-sm text-base-content/60">
{gettext("of %{count} matching requests with map locations",
count: @map_total_count
)}
</span>
<p class="text-sm text-base-content/60"> <p class="text-sm text-base-content/60">
{gettext("Move the map, then search the newly visible area.")} {gettext("Move the map, then search the newly visible area.")}
</p> </p>
@ -680,6 +703,9 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
data-map-retry-label={gettext("Try map again")} data-map-retry-label={gettext("Try map again")}
data-item-label={gettext("Open request")} data-item-label={gettext("Open request")}
data-cluster-label={gettext("%{count} requests", count: "%{count}")} data-cluster-label={gettext("%{count} requests", count: "%{count}")}
data-cluster-max-zoom-label={
gettext("Maximum zoom reached. Matching requests are listed beside the map.")
}
class="aid-map request-discovery-map" class="aid-map request-discovery-map"
/> />
<button <button

View File

@ -179,7 +179,7 @@ msgid "Active"
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts.ex:231 #: lib/who_need_help_web/components/layouts.ex:231
#: lib/who_need_help_web/live/activity_live/index.ex:33 #: lib/who_need_help_web/live/activity_live/index.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities" msgid "Activities"
msgstr "" msgstr ""
@ -215,7 +215,7 @@ msgstr ""
msgid "Activity restored." msgid "Activity restored."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:427 #: lib/who_need_help_web/live/activity_live/index.ex:491
#: lib/who_need_help_web/live/activity_live/new.ex:204 #: lib/who_need_help_web/live/activity_live/new.ex:204
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity type" msgid "Activity type"
@ -232,12 +232,12 @@ msgstr ""
msgid "Administrator" msgid "Administrator"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:429 #: lib/who_need_help_web/live/activity_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All activities" msgid "All activities"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:407 #: lib/who_need_help_web/live/request_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All categories" msgid "All categories"
msgstr "" msgstr ""
@ -252,7 +252,7 @@ msgstr ""
msgid "Another helper already accepted this request." msgid "Another helper already accepted this request."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:422 #: lib/who_need_help_web/live/request_live/index.ex:508
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Any urgency" msgid "Any urgency"
msgstr "" msgstr ""
@ -367,16 +367,16 @@ msgstr ""
msgid "Cancel request" msgid "Cancel request"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:358 #: lib/who_need_help_web/live/activity_live/index.ex:422
#: lib/who_need_help_web/live/activity_live/show.ex:344 #: lib/who_need_help_web/live/activity_live/show.ex:344
#: lib/who_need_help_web/live/request_live/index.ex:327 #: lib/who_need_help_web/live/request_live/index.ex:413
#: lib/who_need_help_web/live/request_live/index.ex:328 #: lib/who_need_help_web/live/request_live/index.ex:414
#: lib/who_need_help_web/live/request_live/show.ex:956 #: lib/who_need_help_web/live/request_live/show.ex:956
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancelled" msgid "Cancelled"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:405 #: lib/who_need_help_web/live/request_live/index.ex:491
#: lib/who_need_help_web/live/request_live/new.ex:302 #: lib/who_need_help_web/live/request_live/new.ex:302
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Category" msgid "Category"
@ -459,7 +459,7 @@ msgstr ""
msgid "Coffee and conversation" msgid "Coffee and conversation"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:372 #: lib/who_need_help_web/live/activity_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
msgstr "" msgstr ""
@ -474,19 +474,19 @@ msgstr ""
msgid "Community helpers" msgid "Community helpers"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:578 #: lib/who_need_help_web/live/activity_live/index.ex:647
#: lib/who_need_help_web/live/activity_live/show.ex:403 #: lib/who_need_help_web/live/activity_live/show.ex:403
#: lib/who_need_help_web/live/activity_live/show.ex:639 #: lib/who_need_help_web/live/activity_live/show.ex:639
#: lib/who_need_help_web/live/activity_live/show.ex:698 #: lib/who_need_help_web/live/activity_live/show.ex:698
#: lib/who_need_help_web/live/request_live/index.ex:565 #: lib/who_need_help_web/live/request_live/index.ex:656
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Community member" msgid "Community member"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:357 #: lib/who_need_help_web/live/activity_live/index.ex:421
#: lib/who_need_help_web/live/activity_live/show.ex:343 #: lib/who_need_help_web/live/activity_live/show.ex:343
#: lib/who_need_help_web/live/request_live/index.ex:325 #: lib/who_need_help_web/live/request_live/index.ex:411
#: lib/who_need_help_web/live/request_live/index.ex:326 #: lib/who_need_help_web/live/request_live/index.ex:412
#: lib/who_need_help_web/live/request_live/show.ex:955 #: lib/who_need_help_web/live/request_live/show.ex:955
#: lib/who_need_help_web/live/request_live/show.ex:974 #: lib/who_need_help_web/live/request_live/show.ex:974
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -537,13 +537,13 @@ msgstr ""
msgid "Confirming..." msgid "Confirming..."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:378 #: lib/who_need_help_web/live/activity_live/index.ex:442
#: lib/who_need_help_web/live/activity_live/new.ex:12 #: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create activity" msgid "Create activity"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:650 #: lib/who_need_help_web/live/activity_live/index.ex:722
#: lib/who_need_help_web/live/activity_live/new.ex:180 #: lib/who_need_help_web/live/activity_live/new.ex:180
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create an activity" msgid "Create an activity"
@ -554,7 +554,7 @@ msgstr ""
msgid "Create category" msgid "Create category"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:356 #: lib/who_need_help_web/live/request_live/index.ex:442
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create urgent request" msgid "Create urgent request"
msgstr "" msgstr ""
@ -704,7 +704,7 @@ msgstr ""
msgid "Explain what is already arranged and what the volunteer needs to do." msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:369 #: lib/who_need_help_web/live/activity_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Find people to join" msgid "Find people to join"
msgstr "" msgstr ""
@ -865,8 +865,8 @@ msgstr ""
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." 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 "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:323 #: lib/who_need_help_web/live/request_live/index.ex:409
#: lib/who_need_help_web/live/request_live/index.ex:324 #: lib/who_need_help_web/live/request_live/index.ex:410
#: lib/who_need_help_web/live/request_live/show.ex:954 #: lib/who_need_help_web/live/request_live/show.ex:954
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "In progress" msgid "In progress"
@ -989,8 +989,8 @@ msgstr ""
msgid "Match and coordinate" msgid "Match and coordinate"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:321 #: lib/who_need_help_web/live/request_live/index.ex:407
#: lib/who_need_help_web/live/request_live/index.ex:322 #: lib/who_need_help_web/live/request_live/index.ex:408
#: lib/who_need_help_web/live/request_live/show.ex:952 #: lib/who_need_help_web/live/request_live/show.ex:952
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Matched" msgid "Matched"
@ -1016,7 +1016,7 @@ msgstr ""
msgid "Medicine requests" msgid "Medicine requests"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:385 #: lib/who_need_help_web/live/activity_live/index.ex:449
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
msgstr "" msgstr ""
@ -1067,12 +1067,12 @@ msgstr ""
msgid "Moderator access is required." msgid "Moderator access is required."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:347 #: lib/who_need_help_web/live/request_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "NEARBY MUTUAL AID" msgid "NEARBY MUTUAL AID"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:34 #: lib/who_need_help_web/live/request_live/index.ex:47
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby help" msgid "Nearby help"
msgstr "" msgstr ""
@ -1153,9 +1153,9 @@ msgid "Not for emergencies"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:748 #: lib/who_need_help_web/live/notification_live.ex:748
#: lib/who_need_help_web/live/request_live/index.ex:311 #: lib/who_need_help_web/live/request_live/index.ex:397
#: lib/who_need_help_web/live/request_live/index.ex:312 #: lib/who_need_help_web/live/request_live/index.ex:398
#: lib/who_need_help_web/live/request_live/index.ex:423 #: lib/who_need_help_web/live/request_live/index.ex:509
#: lib/who_need_help_web/live/request_live/new.ex:449 #: lib/who_need_help_web/live/request_live/new.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:943 #: lib/who_need_help_web/live/request_live/show.ex:943
#: lib/who_need_help_web/live/request_live/show.ex:944 #: lib/who_need_help_web/live/request_live/show.ex:944
@ -1163,7 +1163,7 @@ msgstr ""
msgid "Now" msgid "Now"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:367 #: lib/who_need_help_web/live/activity_live/index.ex:431
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OPTIONAL SOCIAL PLANS" msgid "OPTIONAL SOCIAL PLANS"
msgstr "" msgstr ""
@ -1181,12 +1181,12 @@ msgstr ""
#: lib/who_need_help/notifications/text.ex:140 #: lib/who_need_help/notifications/text.ex:140
#: lib/who_need_help_web/controllers/content_removal_html.ex:21 #: lib/who_need_help_web/controllers/content_removal_html.ex:21
#: lib/who_need_help_web/controllers/support_html.ex:16 #: lib/who_need_help_web/controllers/support_html.ex:16
#: lib/who_need_help_web/live/activity_live/index.ex:356 #: lib/who_need_help_web/live/activity_live/index.ex:420
#: lib/who_need_help_web/live/activity_live/show.ex:342 #: lib/who_need_help_web/live/activity_live/show.ex:342
#: lib/who_need_help_web/live/moderation_live.ex:258 #: lib/who_need_help_web/live/moderation_live.ex:258
#: lib/who_need_help_web/live/report_live.ex:104 #: lib/who_need_help_web/live/report_live.ex:104
#: lib/who_need_help_web/live/request_live/index.ex:319 #: lib/who_need_help_web/live/request_live/index.ex:405
#: lib/who_need_help_web/live/request_live/index.ex:320 #: lib/who_need_help_web/live/request_live/index.ex:406
#: lib/who_need_help_web/live/request_live/show.ex:951 #: lib/who_need_help_web/live/request_live/show.ex:951
#: lib/who_need_help_web/live/support_operations_live.ex:237 #: lib/who_need_help_web/live/support_operations_live.ex:237
#: lib/who_need_help_web/live/support_operations_live.ex:438 #: lib/who_need_help_web/live/support_operations_live.ex:438
@ -1639,9 +1639,9 @@ msgid "Save profile"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:750 #: lib/who_need_help_web/live/notification_live.ex:750
#: lib/who_need_help_web/live/request_live/index.ex:315 #: lib/who_need_help_web/live/request_live/index.ex:401
#: lib/who_need_help_web/live/request_live/index.ex:316 #: lib/who_need_help_web/live/request_live/index.ex:402
#: lib/who_need_help_web/live/request_live/index.ex:425 #: lib/who_need_help_web/live/request_live/index.ex:511
#: lib/who_need_help_web/live/request_live/new.ex:451 #: lib/who_need_help_web/live/request_live/new.ex:451
#: lib/who_need_help_web/live/request_live/show.ex:947 #: lib/who_need_help_web/live/request_live/show.ex:947
#: lib/who_need_help_web/live/request_live/show.ex:948 #: lib/who_need_help_web/live/request_live/show.ex:948
@ -1904,7 +1904,7 @@ msgstr ""
msgid "This interaction is blocked." msgid "This interaction is blocked."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:363 #: lib/who_need_help_web/live/request_live/index.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:1022 #: lib/who_need_help_web/live/request_live/show.ex:1022
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgid "This is not an emergency service. Contact local emergency services for immediate danger."
@ -1952,9 +1952,9 @@ msgid "To see sent emails, visit"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:749 #: lib/who_need_help_web/live/notification_live.ex:749
#: lib/who_need_help_web/live/request_live/index.ex:313 #: lib/who_need_help_web/live/request_live/index.ex:399
#: lib/who_need_help_web/live/request_live/index.ex:314 #: lib/who_need_help_web/live/request_live/index.ex:400
#: lib/who_need_help_web/live/request_live/index.ex:424 #: lib/who_need_help_web/live/request_live/index.ex:510
#: lib/who_need_help_web/live/request_live/new.ex:450 #: lib/who_need_help_web/live/request_live/new.ex:450
#: lib/who_need_help_web/live/request_live/show.ex:945 #: lib/who_need_help_web/live/request_live/show.ex:945
#: lib/who_need_help_web/live/request_live/show.ex:946 #: lib/who_need_help_web/live/request_live/show.ex:946
@ -2029,7 +2029,7 @@ msgid "Update email instructions"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:743 #: lib/who_need_help_web/live/notification_live.ex:743
#: lib/who_need_help_web/live/request_live/index.ex:420 #: lib/who_need_help_web/live/request_live/index.ex:506
#: lib/who_need_help_web/live/request_live/new.ex:447 #: lib/who_need_help_web/live/request_live/new.ex:447
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Urgency" msgid "Urgency"
@ -2174,7 +2174,7 @@ msgstr ""
msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used." msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:348 #: lib/who_need_help_web/live/request_live/index.ex:434
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Who needs help?" msgid "Who needs help?"
msgstr "" msgstr ""
@ -2292,7 +2292,7 @@ msgstr ""
msgid "Your review is revealed only after both participants submit." msgid "Your review is revealed only after both participants submit."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:630 #: lib/who_need_help_web/live/request_live/index.ex:724
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Your trust summary" msgid "Your trust summary"
msgstr "" msgstr ""
@ -2313,17 +2313,17 @@ msgstr ""
msgid "before confirming." msgid "before confirming."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:577 #: lib/who_need_help_web/live/activity_live/index.ex:646
#: lib/who_need_help_web/live/category_proposal_live.ex:252 #: lib/who_need_help_web/live/category_proposal_live.ex:252
#: lib/who_need_help_web/live/moderation_live.ex:372 #: lib/who_need_help_web/live/moderation_live.ex:372
#: lib/who_need_help_web/live/request_live/index.ex:564 #: lib/who_need_help_web/live/request_live/index.ex:655
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "by %{name}" msgid "by %{name}"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:190 #: lib/who_need_help_web/live/profile_live.ex:190
#: lib/who_need_help_web/live/public_profile_live.ex:133 #: lib/who_need_help_web/live/public_profile_live.ex:133
#: lib/who_need_help_web/live/request_live/index.ex:634 #: lib/who_need_help_web/live/request_live/index.ex:728
#: lib/who_need_help_web/live/request_live/show.ex:1374 #: lib/who_need_help_web/live/request_live/show.ex:1374
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "completed" msgid "completed"
@ -2359,7 +2359,7 @@ msgstr ""
msgid "participants only" msgid "participants only"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:638 #: lib/who_need_help_web/live/request_live/index.ex:732
#: lib/who_need_help_web/live/request_live/show.ex:1378 #: lib/who_need_help_web/live/request_live/show.ex:1378
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "people" msgid "people"
@ -2419,7 +2419,7 @@ msgstr ""
#: lib/who_need_help_web/live/activity_live/show.ex:719 #: lib/who_need_help_web/live/activity_live/show.ex:719
#: lib/who_need_help_web/live/profile_live.ex:285 #: lib/who_need_help_web/live/profile_live.ex:285
#: lib/who_need_help_web/live/public_profile_live.ex:98 #: lib/who_need_help_web/live/public_profile_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:642 #: lib/who_need_help_web/live/request_live/index.ex:736
#: lib/who_need_help_web/live/request_live/show.ex:1093 #: lib/who_need_help_web/live/request_live/show.ex:1093
#: lib/who_need_help_web/live/request_live/show.ex:1476 #: lib/who_need_help_web/live/request_live/show.ex:1476
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2452,8 +2452,8 @@ msgstr ""
msgid "Approved" msgid "Approved"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:329 #: lib/who_need_help_web/live/request_live/index.ex:415
#: lib/who_need_help_web/live/request_live/index.ex:330 #: lib/who_need_help_web/live/request_live/index.ex:416
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expired" msgid "Expired"
msgstr "" msgstr ""
@ -2506,8 +2506,8 @@ msgstr ""
msgid "Load earlier messages" msgid "Load earlier messages"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:589 #: lib/who_need_help_web/live/activity_live/index.ex:658
#: lib/who_need_help_web/live/activity_live/index.ex:679 #: lib/who_need_help_web/live/activity_live/index.ex:751
#: lib/who_need_help_web/live/category_proposal_live.ex:284 #: lib/who_need_help_web/live/category_proposal_live.ex:284
#: lib/who_need_help_web/live/leaderboard_live.ex:91 #: lib/who_need_help_web/live/leaderboard_live.ex:91
#: lib/who_need_help_web/live/moderation_live.ex:465 #: lib/who_need_help_web/live/moderation_live.ex:465
@ -2519,7 +2519,7 @@ msgstr ""
#: lib/who_need_help_web/live/profile_live.ex:363 #: lib/who_need_help_web/live/profile_live.ex:363
#: lib/who_need_help_web/live/public_profile_live.ex:178 #: lib/who_need_help_web/live/public_profile_live.ex:178
#: lib/who_need_help_web/live/report_live.ex:98 #: lib/who_need_help_web/live/report_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:699 #: lib/who_need_help_web/live/request_live/index.ex:805
#: lib/who_need_help_web/live/support_operations_live.ex:306 #: lib/who_need_help_web/live/support_operations_live.ex:306
#: lib/who_need_help_web/live/support_operations_live.ex:505 #: lib/who_need_help_web/live/support_operations_live.ex:505
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2527,7 +2527,7 @@ msgid "Load more"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/show.ex:544 #: lib/who_need_help_web/live/activity_live/show.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:610 #: lib/who_need_help_web/live/request_live/index.ex:701
#: lib/who_need_help_web/live/request_live/show.ex:1275 #: lib/who_need_help_web/live/request_live/show.ex:1275
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable." msgid "The map is unavailable in this browser. Request details remain usable."
@ -4516,44 +4516,44 @@ msgstr ""
msgid "Exact point selected. Drag the red pin or click the map to move it." msgid "Exact point selected. Drag the red pin or click the map to move it."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:453 #: lib/who_need_help_web/live/request_live/index.ex:539
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} request in this map area" msgid "%{count} request in this map area"
msgid_plural "%{count} requests in this map area" msgid_plural "%{count} requests in this map area"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: lib/who_need_help_web/live/activity_live/index.ex:438 #: lib/who_need_help_web/live/activity_live/index.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:430 #: lib/who_need_help_web/live/request_live/index.ex:516
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Area or city" msgid "Area or city"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:525 #: lib/who_need_help_web/live/activity_live/index.ex:594
#: lib/who_need_help_web/live/request_live/index.ex:515 #: lib/who_need_help_web/live/request_live/index.ex:606
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Choose an area on the map" msgid "Choose an area on the map"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:350 #: lib/who_need_help_web/live/request_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters." msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:484 #: lib/who_need_help_web/live/activity_live/index.ex:553
#: lib/who_need_help_web/live/request_live/index.ex:474 #: lib/who_need_help_web/live/request_live/index.ex:565
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "List" msgid "List"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:586 #: lib/who_need_help_web/live/request_live/index.ex:677
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Load more in this area" msgid "Load more in this area"
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:5 #: lib/who_need_help_web/components/layouts/root.html.heex:5
#: lib/who_need_help_web/live/activity_live/index.ex:504 #: lib/who_need_help_web/live/activity_live/index.ex:573
#: lib/who_need_help_web/live/request_live/index.ex:494 #: lib/who_need_help_web/live/request_live/index.ex:585
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Map" msgid "Map"
msgstr "" msgstr ""
@ -4563,53 +4563,53 @@ msgstr ""
msgid "Map preview" msgid "Map preview"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:537 #: lib/who_need_help_web/live/activity_live/index.ex:606
#: lib/who_need_help_web/live/request_live/index.ex:527 #: lib/who_need_help_web/live/request_live/index.ex:618
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move or zoom the map to search somewhere else." msgid "Move or zoom the map to search somewhere else."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:471 #: lib/who_need_help_web/live/activity_live/index.ex:540
#: lib/who_need_help_web/live/request_live/index.ex:461 #: lib/who_need_help_web/live/request_live/index.ex:552
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move the map, then search the newly visible area." msgid "Move the map, then search the newly visible area."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:525 #: lib/who_need_help_web/live/request_live/index.ex:616
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open requests in this area" msgid "No open requests in this area"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:517 #: lib/who_need_help_web/live/request_live/index.ex:608
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only requests from the selected map area will be loaded." msgid "Only requests from the selected map area will be loaded."
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:8 #: lib/who_need_help_web/components/layouts/root.html.heex:8
#: lib/who_need_help_web/live/request_live/index.ex:613 #: lib/who_need_help_web/live/request_live/index.ex:704
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open request" msgid "Open request"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:439 #: lib/who_need_help_web/live/activity_live/index.ex:503
#: lib/who_need_help_web/live/request_live/index.ex:431 #: lib/who_need_help_web/live/request_live/index.ex:517
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Optional text search" msgid "Optional text search"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:592 #: lib/who_need_help_web/live/request_live/index.ex:683
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests without map coordinates appear only when their public area description matches the text search." msgid "Requests without map coordinates appear only when their public area description matches the text search."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:475 #: lib/who_need_help_web/live/activity_live/index.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:465 #: lib/who_need_help_web/live/request_live/index.ex:556
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Results layout" msgid "Results layout"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:621 #: lib/who_need_help_web/live/activity_live/index.ex:693
#: lib/who_need_help_web/live/request_live/index.ex:625 #: lib/who_need_help_web/live/request_live/index.ex:719
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search this area" msgid "Search this area"
msgstr "" msgstr ""
@ -4619,21 +4619,21 @@ msgstr ""
msgid "Static preview" msgid "Static preview"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:494 #: lib/who_need_help_web/live/activity_live/index.ex:563
#: lib/who_need_help_web/live/request_live/index.ex:484 #: lib/who_need_help_web/live/request_live/index.ex:575
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Together" msgid "Together"
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:6 #: lib/who_need_help_web/components/layouts/root.html.heex:6
#: lib/who_need_help_web/controllers/page_html/home.html.heex:160 #: lib/who_need_help_web/controllers/page_html/home.html.heex:160
#: lib/who_need_help_web/live/activity_live/index.ex:609 #: lib/who_need_help_web/live/activity_live/index.ex:681
#: lib/who_need_help_web/live/activity_live/new.ex:355 #: lib/who_need_help_web/live/activity_live/new.ex:355
#: lib/who_need_help_web/live/activity_live/new.ex:414 #: lib/who_need_help_web/live/activity_live/new.ex:414
#: lib/who_need_help_web/live/activity_live/show.ex:546 #: lib/who_need_help_web/live/activity_live/show.ex:546
#: lib/who_need_help_web/live/notification_live.ex:602 #: lib/who_need_help_web/live/notification_live.ex:602
#: lib/who_need_help_web/live/notification_live.ex:621 #: lib/who_need_help_web/live/notification_live.ex:621
#: lib/who_need_help_web/live/request_live/index.ex:612 #: lib/who_need_help_web/live/request_live/index.ex:703
#: lib/who_need_help_web/live/request_live/new.ex:485 #: lib/who_need_help_web/live/request_live/new.ex:485
#: lib/who_need_help_web/live/request_live/new.ex:563 #: lib/who_need_help_web/live/request_live/new.ex:563
#: lib/who_need_help_web/live/request_live/show.ex:1277 #: lib/who_need_help_web/live/request_live/show.ex:1277
@ -5022,7 +5022,7 @@ msgid "Nearby alert deleted."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:456 #: lib/who_need_help_web/live/notification_live.ex:456
#: lib/who_need_help_web/live/request_live/index.ex:376 #: lib/who_need_help_web/live/request_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby requests" msgid "Nearby requests"
msgstr "" msgstr ""
@ -5362,7 +5362,7 @@ msgid "WebGL is unavailable in this browser, so the full map cannot start. Selec
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:7 #: lib/who_need_help_web/components/layouts/root.html.heex:7
#: lib/who_need_help_web/live/activity_live/index.ex:610 #: lib/who_need_help_web/live/activity_live/index.ex:682
#: lib/who_need_help_web/live/activity_live/show.ex:547 #: lib/who_need_help_web/live/activity_live/show.ex:547
#: lib/who_need_help_web/live/request_live/show.ex:1278 #: lib/who_need_help_web/live/request_live/show.ex:1278
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -5927,7 +5927,7 @@ msgstr ""
msgid "Delivery settings" msgid "Delivery settings"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:569 #: lib/who_need_help_web/live/request_live/index.ex:660
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expires" msgid "Expires"
msgstr "" msgstr ""
@ -5947,29 +5947,29 @@ msgstr ""
msgid "Notification sections" msgid "Notification sections"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:605 #: lib/who_need_help_web/live/activity_live/index.ex:674
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activities" msgid "%{count} activities"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:463 #: lib/who_need_help_web/live/activity_live/index.ex:527
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activity in this map area" msgid "%{count} activity in this map area"
msgid_plural "%{count} activities in this map area" msgid_plural "%{count} activities in this map area"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: lib/who_need_help_web/live/request_live/index.ex:614 #: lib/who_need_help_web/live/request_live/index.ex:705
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} requests" msgid "%{count} requests"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:645 #: lib/who_need_help_web/live/activity_live/index.ex:717
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list." msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:393 #: lib/who_need_help_web/live/activity_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity views" msgid "Activity views"
msgstr "" msgstr ""
@ -5984,12 +5984,12 @@ msgstr ""
msgid "Confirm the safety guidance before asking nearby people for help." msgid "Confirm the safety guidance before asking nearby people for help."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:659 #: lib/who_need_help_web/live/request_live/index.ex:753
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create request" msgid "Create request"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:673 #: lib/who_need_help_web/live/request_live/index.ex:776
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create your first request" msgid "Create your first request"
msgstr "" msgstr ""
@ -6004,43 +6004,43 @@ msgstr ""
msgid "Location" msgid "Location"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:399 #: lib/who_need_help_web/live/activity_live/index.ex:463
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My activities" msgid "My activities"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:377 #: lib/who_need_help_web/live/request_live/index.ex:463
#: lib/who_need_help_web/live/request_live/index.ex:653 #: lib/who_need_help_web/live/request_live/index.ex:747
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My requests" msgid "My requests"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:398 #: lib/who_need_help_web/live/activity_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby activities" msgid "Nearby activities"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:643 #: lib/who_need_help_web/live/activity_live/index.ex:715
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No activities yet" msgid "No activities yet"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:535 #: lib/who_need_help_web/live/activity_live/index.ex:604
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open activities in this area" msgid "No open activities in this area"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:668 #: lib/who_need_help_web/live/request_live/index.ex:771
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No requests yet" msgid "No requests yet"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:527 #: lib/who_need_help_web/live/activity_live/index.ex:596
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only activities from the selected map area will be loaded." msgid "Only activities from the selected map area will be loaded."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:604 #: lib/who_need_help_web/live/activity_live/index.ex:673
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open activity" msgid "Open activity"
msgstr "" msgstr ""
@ -6050,12 +6050,12 @@ msgstr ""
msgid "Request form steps" msgid "Request form steps"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:371 #: lib/who_need_help_web/live/request_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Request views" msgid "Request views"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:670 #: lib/who_need_help_web/live/request_live/index.ex:773
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests you publish or accept will appear here." msgid "Requests you publish or accept will appear here."
msgstr "" msgstr ""
@ -6080,7 +6080,7 @@ msgstr ""
msgid "Share only as much location detail as helpers need." msgid "Share only as much location detail as helpers need."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:607 #: lib/who_need_help_web/live/activity_live/index.ex:679
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Activity details remain usable." msgid "The map is unavailable in this browser. Activity details remain usable."
msgstr "" msgstr ""
@ -6090,7 +6090,7 @@ msgstr ""
msgid "Timing" msgid "Timing"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:655 #: lib/who_need_help_web/live/request_live/index.ex:749
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Track requests you created and requests you accepted as a helper." msgid "Track requests you created and requests you accepted as a helper."
msgstr "" msgstr ""
@ -6751,11 +6751,32 @@ msgstr ""
msgid "Choose the meeting point on the map or use your current location." msgid "Choose the meeting point on the map or use your current location."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:458 #: lib/who_need_help_web/live/activity_live/index.ex:522
#: lib/who_need_help_web/live/activity_live/index.ex:517 #: lib/who_need_help_web/live/activity_live/index.ex:586
#: lib/who_need_help_web/live/activity_live/index.ex:635 #: lib/who_need_help_web/live/activity_live/index.ex:707
#: lib/who_need_help_web/live/request_live/index.ex:448 #: lib/who_need_help_web/live/request_live/index.ex:534
#: lib/who_need_help_web/live/request_live/index.ex:507 #: lib/who_need_help_web/live/request_live/index.ex:598
#: lib/who_need_help_web/live/request_live/index.ex:763
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Loading results…" msgid "Loading results…"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:676
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching activities are listed beside the map."
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:707
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching requests are listed beside the map."
msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:535
#, elixir-autogen, elixir-format
msgid "of %{count} matching activities with map locations"
msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:547
#, elixir-autogen, elixir-format
msgid "of %{count} matching requests with map locations"
msgstr ""

View File

@ -179,7 +179,7 @@ msgid "Active"
msgstr "Active" msgstr "Active"
#: lib/who_need_help_web/components/layouts.ex:231 #: lib/who_need_help_web/components/layouts.ex:231
#: lib/who_need_help_web/live/activity_live/index.ex:33 #: lib/who_need_help_web/live/activity_live/index.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities" msgid "Activities"
msgstr "Activities" msgstr "Activities"
@ -215,7 +215,7 @@ msgstr "Activity published. You approve every participant."
msgid "Activity restored." msgid "Activity restored."
msgstr "Activity restored." msgstr "Activity restored."
#: lib/who_need_help_web/live/activity_live/index.ex:427 #: lib/who_need_help_web/live/activity_live/index.ex:491
#: lib/who_need_help_web/live/activity_live/new.ex:204 #: lib/who_need_help_web/live/activity_live/new.ex:204
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity type" msgid "Activity type"
@ -232,12 +232,12 @@ msgstr "Add unverified link"
msgid "Administrator" msgid "Administrator"
msgstr "Administrator" msgstr "Administrator"
#: lib/who_need_help_web/live/activity_live/index.ex:429 #: lib/who_need_help_web/live/activity_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All activities" msgid "All activities"
msgstr "All activities" msgstr "All activities"
#: lib/who_need_help_web/live/request_live/index.ex:407 #: lib/who_need_help_web/live/request_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All categories" msgid "All categories"
msgstr "All categories" msgstr "All categories"
@ -252,7 +252,7 @@ msgstr "Already registered?"
msgid "Another helper already accepted this request." msgid "Another helper already accepted this request."
msgstr "Another helper already accepted this request." msgstr "Another helper already accepted this request."
#: lib/who_need_help_web/live/request_live/index.ex:422 #: lib/who_need_help_web/live/request_live/index.ex:508
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Any urgency" msgid "Any urgency"
msgstr "Any urgency" msgstr "Any urgency"
@ -367,16 +367,16 @@ msgstr "Cancel activity"
msgid "Cancel request" msgid "Cancel request"
msgstr "Cancel request" msgstr "Cancel request"
#: lib/who_need_help_web/live/activity_live/index.ex:358 #: lib/who_need_help_web/live/activity_live/index.ex:422
#: lib/who_need_help_web/live/activity_live/show.ex:344 #: lib/who_need_help_web/live/activity_live/show.ex:344
#: lib/who_need_help_web/live/request_live/index.ex:327 #: lib/who_need_help_web/live/request_live/index.ex:413
#: lib/who_need_help_web/live/request_live/index.ex:328 #: lib/who_need_help_web/live/request_live/index.ex:414
#: lib/who_need_help_web/live/request_live/show.ex:956 #: lib/who_need_help_web/live/request_live/show.ex:956
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancelled" msgid "Cancelled"
msgstr "Cancelled" msgstr "Cancelled"
#: lib/who_need_help_web/live/request_live/index.ex:405 #: lib/who_need_help_web/live/request_live/index.ex:491
#: lib/who_need_help_web/live/request_live/new.ex:302 #: lib/who_need_help_web/live/request_live/new.ex:302
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Category" msgid "Category"
@ -459,7 +459,7 @@ msgstr "Close"
msgid "Coffee and conversation" msgid "Coffee and conversation"
msgstr "Coffee and conversation" msgstr "Coffee and conversation"
#: lib/who_need_help_web/live/activity_live/index.ex:372 #: lib/who_need_help_web/live/activity_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
msgstr "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgstr "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
@ -474,19 +474,19 @@ msgstr "Comment (optional)"
msgid "Community helpers" msgid "Community helpers"
msgstr "Community helpers" msgstr "Community helpers"
#: lib/who_need_help_web/live/activity_live/index.ex:578 #: lib/who_need_help_web/live/activity_live/index.ex:647
#: lib/who_need_help_web/live/activity_live/show.ex:403 #: lib/who_need_help_web/live/activity_live/show.ex:403
#: lib/who_need_help_web/live/activity_live/show.ex:639 #: lib/who_need_help_web/live/activity_live/show.ex:639
#: lib/who_need_help_web/live/activity_live/show.ex:698 #: lib/who_need_help_web/live/activity_live/show.ex:698
#: lib/who_need_help_web/live/request_live/index.ex:565 #: lib/who_need_help_web/live/request_live/index.ex:656
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Community member" msgid "Community member"
msgstr "Community member" msgstr "Community member"
#: lib/who_need_help_web/live/activity_live/index.ex:357 #: lib/who_need_help_web/live/activity_live/index.ex:421
#: lib/who_need_help_web/live/activity_live/show.ex:343 #: lib/who_need_help_web/live/activity_live/show.ex:343
#: lib/who_need_help_web/live/request_live/index.ex:325 #: lib/who_need_help_web/live/request_live/index.ex:411
#: lib/who_need_help_web/live/request_live/index.ex:326 #: lib/who_need_help_web/live/request_live/index.ex:412
#: lib/who_need_help_web/live/request_live/show.ex:955 #: lib/who_need_help_web/live/request_live/show.ex:955
#: lib/who_need_help_web/live/request_live/show.ex:974 #: lib/who_need_help_web/live/request_live/show.ex:974
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -537,13 +537,13 @@ msgstr "Confirmation instructions"
msgid "Confirming..." msgid "Confirming..."
msgstr "Confirming..." msgstr "Confirming..."
#: lib/who_need_help_web/live/activity_live/index.ex:378 #: lib/who_need_help_web/live/activity_live/index.ex:442
#: lib/who_need_help_web/live/activity_live/new.ex:12 #: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create activity" msgid "Create activity"
msgstr "Create activity" msgstr "Create activity"
#: lib/who_need_help_web/live/activity_live/index.ex:650 #: lib/who_need_help_web/live/activity_live/index.ex:722
#: lib/who_need_help_web/live/activity_live/new.ex:180 #: lib/who_need_help_web/live/activity_live/new.ex:180
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create an activity" msgid "Create an activity"
@ -554,7 +554,7 @@ msgstr "Create an activity"
msgid "Create category" msgid "Create category"
msgstr "Create category" msgstr "Create category"
#: lib/who_need_help_web/live/request_live/index.ex:356 #: lib/who_need_help_web/live/request_live/index.ex:442
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create urgent request" msgid "Create urgent request"
msgstr "Create urgent request" msgstr "Create urgent request"
@ -704,7 +704,7 @@ msgstr "Expires:"
msgid "Explain what is already arranged and what the volunteer needs to do." msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "Explain what is already arranged and what the volunteer needs to do." msgstr "Explain what is already arranged and what the volunteer needs to do."
#: lib/who_need_help_web/live/activity_live/index.ex:369 #: lib/who_need_help_web/live/activity_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Find people to join" msgid "Find people to join"
msgstr "Find people to join" msgstr "Find people to join"
@ -865,8 +865,8 @@ msgstr "Impersonation"
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." 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 "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 "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."
#: lib/who_need_help_web/live/request_live/index.ex:323 #: lib/who_need_help_web/live/request_live/index.ex:409
#: lib/who_need_help_web/live/request_live/index.ex:324 #: lib/who_need_help_web/live/request_live/index.ex:410
#: lib/who_need_help_web/live/request_live/show.ex:954 #: lib/who_need_help_web/live/request_live/show.ex:954
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "In progress" msgid "In progress"
@ -989,8 +989,8 @@ msgstr "Mark completed"
msgid "Match and coordinate" msgid "Match and coordinate"
msgstr "Match and coordinate" msgstr "Match and coordinate"
#: lib/who_need_help_web/live/request_live/index.ex:321 #: lib/who_need_help_web/live/request_live/index.ex:407
#: lib/who_need_help_web/live/request_live/index.ex:322 #: lib/who_need_help_web/live/request_live/index.ex:408
#: lib/who_need_help_web/live/request_live/show.ex:952 #: lib/who_need_help_web/live/request_live/show.ex:952
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Matched" msgid "Matched"
@ -1016,7 +1016,7 @@ msgstr "Medicine pickup remains the first priority. Roadside categories use the
msgid "Medicine requests" msgid "Medicine requests"
msgstr "Medicine requests" msgstr "Medicine requests"
#: lib/who_need_help_web/live/activity_live/index.ex:385 #: lib/who_need_help_web/live/activity_live/index.ex:449
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
msgstr "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgstr "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
@ -1067,12 +1067,12 @@ msgstr "Moderator"
msgid "Moderator access is required." msgid "Moderator access is required."
msgstr "Moderator access is required." msgstr "Moderator access is required."
#: lib/who_need_help_web/live/request_live/index.ex:347 #: lib/who_need_help_web/live/request_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "NEARBY MUTUAL AID" msgid "NEARBY MUTUAL AID"
msgstr "NEARBY MUTUAL AID" msgstr "NEARBY MUTUAL AID"
#: lib/who_need_help_web/live/request_live/index.ex:34 #: lib/who_need_help_web/live/request_live/index.ex:47
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby help" msgid "Nearby help"
msgstr "Nearby help" msgstr "Nearby help"
@ -1153,9 +1153,9 @@ msgid "Not for emergencies"
msgstr "Not for emergencies" msgstr "Not for emergencies"
#: lib/who_need_help_web/live/notification_live.ex:748 #: lib/who_need_help_web/live/notification_live.ex:748
#: lib/who_need_help_web/live/request_live/index.ex:311 #: lib/who_need_help_web/live/request_live/index.ex:397
#: lib/who_need_help_web/live/request_live/index.ex:312 #: lib/who_need_help_web/live/request_live/index.ex:398
#: lib/who_need_help_web/live/request_live/index.ex:423 #: lib/who_need_help_web/live/request_live/index.ex:509
#: lib/who_need_help_web/live/request_live/new.ex:449 #: lib/who_need_help_web/live/request_live/new.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:943 #: lib/who_need_help_web/live/request_live/show.ex:943
#: lib/who_need_help_web/live/request_live/show.ex:944 #: lib/who_need_help_web/live/request_live/show.ex:944
@ -1163,7 +1163,7 @@ msgstr "Not for emergencies"
msgid "Now" msgid "Now"
msgstr "Now" msgstr "Now"
#: lib/who_need_help_web/live/activity_live/index.ex:367 #: lib/who_need_help_web/live/activity_live/index.ex:431
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OPTIONAL SOCIAL PLANS" msgid "OPTIONAL SOCIAL PLANS"
msgstr "OPTIONAL SOCIAL PLANS" msgstr "OPTIONAL SOCIAL PLANS"
@ -1181,12 +1181,12 @@ msgstr "Only the organizer and approved participants can read or send these mess
#: lib/who_need_help/notifications/text.ex:140 #: lib/who_need_help/notifications/text.ex:140
#: lib/who_need_help_web/controllers/content_removal_html.ex:21 #: lib/who_need_help_web/controllers/content_removal_html.ex:21
#: lib/who_need_help_web/controllers/support_html.ex:16 #: lib/who_need_help_web/controllers/support_html.ex:16
#: lib/who_need_help_web/live/activity_live/index.ex:356 #: lib/who_need_help_web/live/activity_live/index.ex:420
#: lib/who_need_help_web/live/activity_live/show.ex:342 #: lib/who_need_help_web/live/activity_live/show.ex:342
#: lib/who_need_help_web/live/moderation_live.ex:258 #: lib/who_need_help_web/live/moderation_live.ex:258
#: lib/who_need_help_web/live/report_live.ex:104 #: lib/who_need_help_web/live/report_live.ex:104
#: lib/who_need_help_web/live/request_live/index.ex:319 #: lib/who_need_help_web/live/request_live/index.ex:405
#: lib/who_need_help_web/live/request_live/index.ex:320 #: lib/who_need_help_web/live/request_live/index.ex:406
#: lib/who_need_help_web/live/request_live/show.ex:951 #: lib/who_need_help_web/live/request_live/show.ex:951
#: lib/who_need_help_web/live/support_operations_live.ex:237 #: lib/who_need_help_web/live/support_operations_live.ex:237
#: lib/who_need_help_web/live/support_operations_live.ex:438 #: lib/who_need_help_web/live/support_operations_live.ex:438
@ -1639,9 +1639,9 @@ msgid "Save profile"
msgstr "Save profile" msgstr "Save profile"
#: lib/who_need_help_web/live/notification_live.ex:750 #: lib/who_need_help_web/live/notification_live.ex:750
#: lib/who_need_help_web/live/request_live/index.ex:315 #: lib/who_need_help_web/live/request_live/index.ex:401
#: lib/who_need_help_web/live/request_live/index.ex:316 #: lib/who_need_help_web/live/request_live/index.ex:402
#: lib/who_need_help_web/live/request_live/index.ex:425 #: lib/who_need_help_web/live/request_live/index.ex:511
#: lib/who_need_help_web/live/request_live/new.ex:451 #: lib/who_need_help_web/live/request_live/new.ex:451
#: lib/who_need_help_web/live/request_live/show.ex:947 #: lib/who_need_help_web/live/request_live/show.ex:947
#: lib/who_need_help_web/live/request_live/show.ex:948 #: lib/who_need_help_web/live/request_live/show.ex:948
@ -1904,7 +1904,7 @@ msgstr "This activity state has already changed."
msgid "This interaction is blocked." msgid "This interaction is blocked."
msgstr "This interaction is blocked." msgstr "This interaction is blocked."
#: lib/who_need_help_web/live/request_live/index.ex:363 #: lib/who_need_help_web/live/request_live/index.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:1022 #: lib/who_need_help_web/live/request_live/show.ex:1022
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgid "This is not an emergency service. Contact local emergency services for immediate danger."
@ -1952,9 +1952,9 @@ msgid "To see sent emails, visit"
msgstr "To see sent emails, visit" msgstr "To see sent emails, visit"
#: lib/who_need_help_web/live/notification_live.ex:749 #: lib/who_need_help_web/live/notification_live.ex:749
#: lib/who_need_help_web/live/request_live/index.ex:313 #: lib/who_need_help_web/live/request_live/index.ex:399
#: lib/who_need_help_web/live/request_live/index.ex:314 #: lib/who_need_help_web/live/request_live/index.ex:400
#: lib/who_need_help_web/live/request_live/index.ex:424 #: lib/who_need_help_web/live/request_live/index.ex:510
#: lib/who_need_help_web/live/request_live/new.ex:450 #: lib/who_need_help_web/live/request_live/new.ex:450
#: lib/who_need_help_web/live/request_live/show.ex:945 #: lib/who_need_help_web/live/request_live/show.ex:945
#: lib/who_need_help_web/live/request_live/show.ex:946 #: lib/who_need_help_web/live/request_live/show.ex:946
@ -2029,7 +2029,7 @@ msgid "Update email instructions"
msgstr "Update email instructions" msgstr "Update email instructions"
#: lib/who_need_help_web/live/notification_live.ex:743 #: lib/who_need_help_web/live/notification_live.ex:743
#: lib/who_need_help_web/live/request_live/index.ex:420 #: lib/who_need_help_web/live/request_live/index.ex:506
#: lib/who_need_help_web/live/request_live/new.ex:447 #: lib/who_need_help_web/live/request_live/new.ex:447
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Urgency" msgid "Urgency"
@ -2174,7 +2174,7 @@ msgstr "Who Need Help home"
msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used." msgid "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used."
msgstr "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used." msgstr "Who Need Help was developed with the local Codex CLI authenticated through the builder's ChatGPT subscription. No OpenAI API key or usage-based API fallback is used."
#: lib/who_need_help_web/live/request_live/index.ex:348 #: lib/who_need_help_web/live/request_live/index.ex:434
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Who needs help?" msgid "Who needs help?"
msgstr "Who needs help?" msgstr "Who needs help?"
@ -2292,7 +2292,7 @@ msgstr "Your requests"
msgid "Your review is revealed only after both participants submit." msgid "Your review is revealed only after both participants submit."
msgstr "Your review is revealed only after both participants submit." msgstr "Your review is revealed only after both participants submit."
#: lib/who_need_help_web/live/request_live/index.ex:630 #: lib/who_need_help_web/live/request_live/index.ex:724
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Your trust summary" msgid "Your trust summary"
msgstr "Your trust summary" msgstr "Your trust summary"
@ -2313,17 +2313,17 @@ msgstr "activity message"
msgid "before confirming." msgid "before confirming."
msgstr "before confirming." msgstr "before confirming."
#: lib/who_need_help_web/live/activity_live/index.ex:577 #: lib/who_need_help_web/live/activity_live/index.ex:646
#: lib/who_need_help_web/live/category_proposal_live.ex:252 #: lib/who_need_help_web/live/category_proposal_live.ex:252
#: lib/who_need_help_web/live/moderation_live.ex:372 #: lib/who_need_help_web/live/moderation_live.ex:372
#: lib/who_need_help_web/live/request_live/index.ex:564 #: lib/who_need_help_web/live/request_live/index.ex:655
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "by %{name}" msgid "by %{name}"
msgstr "by %{name}" msgstr "by %{name}"
#: lib/who_need_help_web/live/profile_live.ex:190 #: lib/who_need_help_web/live/profile_live.ex:190
#: lib/who_need_help_web/live/public_profile_live.ex:133 #: lib/who_need_help_web/live/public_profile_live.ex:133
#: lib/who_need_help_web/live/request_live/index.ex:634 #: lib/who_need_help_web/live/request_live/index.ex:728
#: lib/who_need_help_web/live/request_live/show.ex:1374 #: lib/who_need_help_web/live/request_live/show.ex:1374
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "completed" msgid "completed"
@ -2359,7 +2359,7 @@ msgstr "organizer"
msgid "participants only" msgid "participants only"
msgstr "participants only" msgstr "participants only"
#: lib/who_need_help_web/live/request_live/index.ex:638 #: lib/who_need_help_web/live/request_live/index.ex:732
#: lib/who_need_help_web/live/request_live/show.ex:1378 #: lib/who_need_help_web/live/request_live/show.ex:1378
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "people" msgid "people"
@ -2419,7 +2419,7 @@ msgstr "urgent"
#: lib/who_need_help_web/live/activity_live/show.ex:719 #: lib/who_need_help_web/live/activity_live/show.ex:719
#: lib/who_need_help_web/live/profile_live.ex:285 #: lib/who_need_help_web/live/profile_live.ex:285
#: lib/who_need_help_web/live/public_profile_live.ex:98 #: lib/who_need_help_web/live/public_profile_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:642 #: lib/who_need_help_web/live/request_live/index.ex:736
#: lib/who_need_help_web/live/request_live/show.ex:1093 #: lib/who_need_help_web/live/request_live/show.ex:1093
#: lib/who_need_help_web/live/request_live/show.ex:1476 #: lib/who_need_help_web/live/request_live/show.ex:1476
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2452,8 +2452,8 @@ msgstr "← Requests"
msgid "Approved" msgid "Approved"
msgstr "Approved" msgstr "Approved"
#: lib/who_need_help_web/live/request_live/index.ex:329 #: lib/who_need_help_web/live/request_live/index.ex:415
#: lib/who_need_help_web/live/request_live/index.ex:330 #: lib/who_need_help_web/live/request_live/index.ex:416
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expired" msgid "Expired"
msgstr "Expired" msgstr "Expired"
@ -2506,8 +2506,8 @@ msgstr "Unusual action rate"
msgid "Load earlier messages" msgid "Load earlier messages"
msgstr "Load earlier messages" msgstr "Load earlier messages"
#: lib/who_need_help_web/live/activity_live/index.ex:589 #: lib/who_need_help_web/live/activity_live/index.ex:658
#: lib/who_need_help_web/live/activity_live/index.ex:679 #: lib/who_need_help_web/live/activity_live/index.ex:751
#: lib/who_need_help_web/live/category_proposal_live.ex:284 #: lib/who_need_help_web/live/category_proposal_live.ex:284
#: lib/who_need_help_web/live/leaderboard_live.ex:91 #: lib/who_need_help_web/live/leaderboard_live.ex:91
#: lib/who_need_help_web/live/moderation_live.ex:465 #: lib/who_need_help_web/live/moderation_live.ex:465
@ -2519,7 +2519,7 @@ msgstr "Load earlier messages"
#: lib/who_need_help_web/live/profile_live.ex:363 #: lib/who_need_help_web/live/profile_live.ex:363
#: lib/who_need_help_web/live/public_profile_live.ex:178 #: lib/who_need_help_web/live/public_profile_live.ex:178
#: lib/who_need_help_web/live/report_live.ex:98 #: lib/who_need_help_web/live/report_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:699 #: lib/who_need_help_web/live/request_live/index.ex:805
#: lib/who_need_help_web/live/support_operations_live.ex:306 #: lib/who_need_help_web/live/support_operations_live.ex:306
#: lib/who_need_help_web/live/support_operations_live.ex:505 #: lib/who_need_help_web/live/support_operations_live.ex:505
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2527,7 +2527,7 @@ msgid "Load more"
msgstr "Load more" msgstr "Load more"
#: lib/who_need_help_web/live/activity_live/show.ex:544 #: lib/who_need_help_web/live/activity_live/show.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:610 #: lib/who_need_help_web/live/request_live/index.ex:701
#: lib/who_need_help_web/live/request_live/show.ex:1275 #: lib/who_need_help_web/live/request_live/show.ex:1275
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable." msgid "The map is unavailable in this browser. Request details remain usable."
@ -4516,44 +4516,44 @@ msgstr ""
msgid "Exact point selected. Drag the red pin or click the map to move it." msgid "Exact point selected. Drag the red pin or click the map to move it."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:453 #: lib/who_need_help_web/live/request_live/index.ex:539
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} request in this map area" msgid "%{count} request in this map area"
msgid_plural "%{count} requests in this map area" msgid_plural "%{count} requests in this map area"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: lib/who_need_help_web/live/activity_live/index.ex:438 #: lib/who_need_help_web/live/activity_live/index.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:430 #: lib/who_need_help_web/live/request_live/index.ex:516
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Area or city" msgid "Area or city"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:525 #: lib/who_need_help_web/live/activity_live/index.ex:594
#: lib/who_need_help_web/live/request_live/index.ex:515 #: lib/who_need_help_web/live/request_live/index.ex:606
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Choose an area on the map" msgid "Choose an area on the map"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:350 #: lib/who_need_help_web/live/request_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters." msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:484 #: lib/who_need_help_web/live/activity_live/index.ex:553
#: lib/who_need_help_web/live/request_live/index.ex:474 #: lib/who_need_help_web/live/request_live/index.ex:565
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "List" msgid "List"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:586 #: lib/who_need_help_web/live/request_live/index.ex:677
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Load more in this area" msgid "Load more in this area"
msgstr "Load more in this area" msgstr "Load more in this area"
#: lib/who_need_help_web/components/layouts/root.html.heex:5 #: lib/who_need_help_web/components/layouts/root.html.heex:5
#: lib/who_need_help_web/live/activity_live/index.ex:504 #: lib/who_need_help_web/live/activity_live/index.ex:573
#: lib/who_need_help_web/live/request_live/index.ex:494 #: lib/who_need_help_web/live/request_live/index.ex:585
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Map" msgid "Map"
msgstr "" msgstr ""
@ -4563,53 +4563,53 @@ msgstr ""
msgid "Map preview" msgid "Map preview"
msgstr "Map preview" msgstr "Map preview"
#: lib/who_need_help_web/live/activity_live/index.ex:537 #: lib/who_need_help_web/live/activity_live/index.ex:606
#: lib/who_need_help_web/live/request_live/index.ex:527 #: lib/who_need_help_web/live/request_live/index.ex:618
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move or zoom the map to search somewhere else." msgid "Move or zoom the map to search somewhere else."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:471 #: lib/who_need_help_web/live/activity_live/index.ex:540
#: lib/who_need_help_web/live/request_live/index.ex:461 #: lib/who_need_help_web/live/request_live/index.ex:552
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move the map, then search the newly visible area." msgid "Move the map, then search the newly visible area."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/request_live/index.ex:525 #: lib/who_need_help_web/live/request_live/index.ex:616
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open requests in this area" msgid "No open requests in this area"
msgstr "No open requests in this area" msgstr "No open requests in this area"
#: lib/who_need_help_web/live/request_live/index.ex:517 #: lib/who_need_help_web/live/request_live/index.ex:608
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only requests from the selected map area will be loaded." msgid "Only requests from the selected map area will be loaded."
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:8 #: lib/who_need_help_web/components/layouts/root.html.heex:8
#: lib/who_need_help_web/live/request_live/index.ex:613 #: lib/who_need_help_web/live/request_live/index.ex:704
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open request" msgid "Open request"
msgstr "Open request" msgstr "Open request"
#: lib/who_need_help_web/live/activity_live/index.ex:439 #: lib/who_need_help_web/live/activity_live/index.ex:503
#: lib/who_need_help_web/live/request_live/index.ex:431 #: lib/who_need_help_web/live/request_live/index.ex:517
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Optional text search" msgid "Optional text search"
msgstr "Optional text search" msgstr "Optional text search"
#: lib/who_need_help_web/live/request_live/index.ex:592 #: lib/who_need_help_web/live/request_live/index.ex:683
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests without map coordinates appear only when their public area description matches the text search." msgid "Requests without map coordinates appear only when their public area description matches the text search."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:475 #: lib/who_need_help_web/live/activity_live/index.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:465 #: lib/who_need_help_web/live/request_live/index.ex:556
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Results layout" msgid "Results layout"
msgstr "" msgstr ""
#: lib/who_need_help_web/live/activity_live/index.ex:621 #: lib/who_need_help_web/live/activity_live/index.ex:693
#: lib/who_need_help_web/live/request_live/index.ex:625 #: lib/who_need_help_web/live/request_live/index.ex:719
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search this area" msgid "Search this area"
msgstr "" msgstr ""
@ -4619,21 +4619,21 @@ msgstr ""
msgid "Static preview" msgid "Static preview"
msgstr "Static preview" msgstr "Static preview"
#: lib/who_need_help_web/live/activity_live/index.ex:494 #: lib/who_need_help_web/live/activity_live/index.ex:563
#: lib/who_need_help_web/live/request_live/index.ex:484 #: lib/who_need_help_web/live/request_live/index.ex:575
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Together" msgid "Together"
msgstr "" msgstr ""
#: lib/who_need_help_web/components/layouts/root.html.heex:6 #: lib/who_need_help_web/components/layouts/root.html.heex:6
#: lib/who_need_help_web/controllers/page_html/home.html.heex:160 #: lib/who_need_help_web/controllers/page_html/home.html.heex:160
#: lib/who_need_help_web/live/activity_live/index.ex:609 #: lib/who_need_help_web/live/activity_live/index.ex:681
#: lib/who_need_help_web/live/activity_live/new.ex:355 #: lib/who_need_help_web/live/activity_live/new.ex:355
#: lib/who_need_help_web/live/activity_live/new.ex:414 #: lib/who_need_help_web/live/activity_live/new.ex:414
#: lib/who_need_help_web/live/activity_live/show.ex:546 #: lib/who_need_help_web/live/activity_live/show.ex:546
#: lib/who_need_help_web/live/notification_live.ex:602 #: lib/who_need_help_web/live/notification_live.ex:602
#: lib/who_need_help_web/live/notification_live.ex:621 #: lib/who_need_help_web/live/notification_live.ex:621
#: lib/who_need_help_web/live/request_live/index.ex:612 #: lib/who_need_help_web/live/request_live/index.ex:703
#: lib/who_need_help_web/live/request_live/new.ex:485 #: lib/who_need_help_web/live/request_live/new.ex:485
#: lib/who_need_help_web/live/request_live/new.ex:563 #: lib/who_need_help_web/live/request_live/new.ex:563
#: lib/who_need_help_web/live/request_live/show.ex:1277 #: lib/who_need_help_web/live/request_live/show.ex:1277
@ -5022,7 +5022,7 @@ msgid "Nearby alert deleted."
msgstr "" msgstr ""
#: lib/who_need_help_web/live/notification_live.ex:456 #: lib/who_need_help_web/live/notification_live.ex:456
#: lib/who_need_help_web/live/request_live/index.ex:376 #: lib/who_need_help_web/live/request_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby requests" msgid "Nearby requests"
msgstr "" msgstr ""
@ -5362,7 +5362,7 @@ msgid "WebGL is unavailable in this browser, so the full map cannot start. Selec
msgstr "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map." msgstr "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
#: lib/who_need_help_web/components/layouts/root.html.heex:7 #: lib/who_need_help_web/components/layouts/root.html.heex:7
#: lib/who_need_help_web/live/activity_live/index.ex:610 #: lib/who_need_help_web/live/activity_live/index.ex:682
#: lib/who_need_help_web/live/activity_live/show.ex:547 #: lib/who_need_help_web/live/activity_live/show.ex:547
#: lib/who_need_help_web/live/request_live/show.ex:1278 #: lib/who_need_help_web/live/request_live/show.ex:1278
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -5927,7 +5927,7 @@ msgstr "Your review is now visible"
msgid "Delivery settings" msgid "Delivery settings"
msgstr "Delivery settings" msgstr "Delivery settings"
#: lib/who_need_help_web/live/request_live/index.ex:569 #: lib/who_need_help_web/live/request_live/index.ex:660
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expires" msgid "Expires"
msgstr "Expires" msgstr "Expires"
@ -5947,29 +5947,29 @@ msgstr "Nearby alerts"
msgid "Notification sections" msgid "Notification sections"
msgstr "Notification sections" msgstr "Notification sections"
#: lib/who_need_help_web/live/activity_live/index.ex:605 #: lib/who_need_help_web/live/activity_live/index.ex:674
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activities" msgid "%{count} activities"
msgstr "%{count} activities" msgstr "%{count} activities"
#: lib/who_need_help_web/live/activity_live/index.ex:463 #: lib/who_need_help_web/live/activity_live/index.ex:527
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activity in this map area" msgid "%{count} activity in this map area"
msgid_plural "%{count} activities in this map area" msgid_plural "%{count} activities in this map area"
msgstr[0] "%{count} activity in this map area" msgstr[0] "%{count} activity in this map area"
msgstr[1] "%{count} activities in this map area" msgstr[1] "%{count} activities in this map area"
#: lib/who_need_help_web/live/request_live/index.ex:614 #: lib/who_need_help_web/live/request_live/index.ex:705
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} requests" msgid "%{count} requests"
msgstr "%{count} requests" msgstr "%{count} requests"
#: lib/who_need_help_web/live/activity_live/index.ex:645 #: lib/who_need_help_web/live/activity_live/index.ex:717
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list." msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list."
msgstr "Activities you create or ask to join will appear here. Leaving removes an activity from this list." msgstr "Activities you create or ask to join will appear here. Leaving removes an activity from this list."
#: lib/who_need_help_web/live/activity_live/index.ex:393 #: lib/who_need_help_web/live/activity_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity views" msgid "Activity views"
msgstr "Activity views" msgstr "Activity views"
@ -5984,12 +5984,12 @@ msgstr "Choose a category and describe the task clearly."
msgid "Confirm the safety guidance before asking nearby people for help." msgid "Confirm the safety guidance before asking nearby people for help."
msgstr "Confirm the safety guidance before asking nearby people for help." msgstr "Confirm the safety guidance before asking nearby people for help."
#: lib/who_need_help_web/live/request_live/index.ex:659 #: lib/who_need_help_web/live/request_live/index.ex:753
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create request" msgid "Create request"
msgstr "Create request" msgstr "Create request"
#: lib/who_need_help_web/live/request_live/index.ex:673 #: lib/who_need_help_web/live/request_live/index.ex:776
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create your first request" msgid "Create your first request"
msgstr "Create your first request" msgstr "Create your first request"
@ -6004,43 +6004,43 @@ msgstr "Help details"
msgid "Location" msgid "Location"
msgstr "Location" msgstr "Location"
#: lib/who_need_help_web/live/activity_live/index.ex:399 #: lib/who_need_help_web/live/activity_live/index.ex:463
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My activities" msgid "My activities"
msgstr "My activities" msgstr "My activities"
#: lib/who_need_help_web/live/request_live/index.ex:377 #: lib/who_need_help_web/live/request_live/index.ex:463
#: lib/who_need_help_web/live/request_live/index.ex:653 #: lib/who_need_help_web/live/request_live/index.ex:747
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My requests" msgid "My requests"
msgstr "My requests" msgstr "My requests"
#: lib/who_need_help_web/live/activity_live/index.ex:398 #: lib/who_need_help_web/live/activity_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby activities" msgid "Nearby activities"
msgstr "Nearby activities" msgstr "Nearby activities"
#: lib/who_need_help_web/live/activity_live/index.ex:643 #: lib/who_need_help_web/live/activity_live/index.ex:715
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No activities yet" msgid "No activities yet"
msgstr "No activities yet" msgstr "No activities yet"
#: lib/who_need_help_web/live/activity_live/index.ex:535 #: lib/who_need_help_web/live/activity_live/index.ex:604
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open activities in this area" msgid "No open activities in this area"
msgstr "No open activities in this area" msgstr "No open activities in this area"
#: lib/who_need_help_web/live/request_live/index.ex:668 #: lib/who_need_help_web/live/request_live/index.ex:771
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No requests yet" msgid "No requests yet"
msgstr "No requests yet" msgstr "No requests yet"
#: lib/who_need_help_web/live/activity_live/index.ex:527 #: lib/who_need_help_web/live/activity_live/index.ex:596
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only activities from the selected map area will be loaded." msgid "Only activities from the selected map area will be loaded."
msgstr "Only activities from the selected map area will be loaded." msgstr "Only activities from the selected map area will be loaded."
#: lib/who_need_help_web/live/activity_live/index.ex:604 #: lib/who_need_help_web/live/activity_live/index.ex:673
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open activity" msgid "Open activity"
msgstr "Open activity" msgstr "Open activity"
@ -6050,12 +6050,12 @@ msgstr "Open activity"
msgid "Request form steps" msgid "Request form steps"
msgstr "Request form steps" msgstr "Request form steps"
#: lib/who_need_help_web/live/request_live/index.ex:371 #: lib/who_need_help_web/live/request_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Request views" msgid "Request views"
msgstr "Request views" msgstr "Request views"
#: lib/who_need_help_web/live/request_live/index.ex:670 #: lib/who_need_help_web/live/request_live/index.ex:773
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests you publish or accept will appear here." msgid "Requests you publish or accept will appear here."
msgstr "Requests you publish or accept will appear here." msgstr "Requests you publish or accept will appear here."
@ -6080,7 +6080,7 @@ msgstr "Set urgency and when this request should close."
msgid "Share only as much location detail as helpers need." msgid "Share only as much location detail as helpers need."
msgstr "Share only as much location detail as helpers need." msgstr "Share only as much location detail as helpers need."
#: lib/who_need_help_web/live/activity_live/index.ex:607 #: lib/who_need_help_web/live/activity_live/index.ex:679
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Activity details remain usable." msgid "The map is unavailable in this browser. Activity details remain usable."
msgstr "The map is unavailable in this browser. Activity details remain usable." msgstr "The map is unavailable in this browser. Activity details remain usable."
@ -6090,7 +6090,7 @@ msgstr "The map is unavailable in this browser. Activity details remain usable."
msgid "Timing" msgid "Timing"
msgstr "Timing" msgstr "Timing"
#: lib/who_need_help_web/live/request_live/index.ex:655 #: lib/who_need_help_web/live/request_live/index.ex:749
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Track requests you created and requests you accepted as a helper." msgid "Track requests you created and requests you accepted as a helper."
msgstr "Track requests you created and requests you accepted as a helper." msgstr "Track requests you created and requests you accepted as a helper."
@ -6751,11 +6751,32 @@ msgstr "What should people see before approval?"
msgid "Choose the meeting point on the map or use your current location." msgid "Choose the meeting point on the map or use your current location."
msgstr "Choose the meeting point on the map or use your current location." msgstr "Choose the meeting point on the map or use your current location."
#: lib/who_need_help_web/live/activity_live/index.ex:458 #: lib/who_need_help_web/live/activity_live/index.ex:522
#: lib/who_need_help_web/live/activity_live/index.ex:517 #: lib/who_need_help_web/live/activity_live/index.ex:586
#: lib/who_need_help_web/live/activity_live/index.ex:635 #: lib/who_need_help_web/live/activity_live/index.ex:707
#: lib/who_need_help_web/live/request_live/index.ex:448 #: lib/who_need_help_web/live/request_live/index.ex:534
#: lib/who_need_help_web/live/request_live/index.ex:507 #: lib/who_need_help_web/live/request_live/index.ex:598
#: lib/who_need_help_web/live/request_live/index.ex:763
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Loading results…" msgid "Loading results…"
msgstr "Loading results…" msgstr "Loading results…"
#: lib/who_need_help_web/live/activity_live/index.ex:676
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching activities are listed beside the map."
msgstr "Maximum zoom reached. Matching activities are listed beside the map."
#: lib/who_need_help_web/live/request_live/index.ex:707
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching requests are listed beside the map."
msgstr "Maximum zoom reached. Matching requests are listed beside the map."
#: lib/who_need_help_web/live/activity_live/index.ex:535
#, elixir-autogen, elixir-format
msgid "of %{count} matching activities with map locations"
msgstr "of %{count} matching activities with map locations"
#: lib/who_need_help_web/live/request_live/index.ex:547
#, elixir-autogen, elixir-format
msgid "of %{count} matching requests with map locations"
msgstr "of %{count} matching requests with map locations"

View File

@ -184,7 +184,7 @@ msgid "Active"
msgstr "Активный" msgstr "Активный"
#: lib/who_need_help_web/components/layouts.ex:231 #: lib/who_need_help_web/components/layouts.ex:231
#: lib/who_need_help_web/live/activity_live/index.ex:33 #: lib/who_need_help_web/live/activity_live/index.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities" msgid "Activities"
msgstr "Активности" msgstr "Активности"
@ -226,7 +226,7 @@ msgstr "Активность опубликована. Вы одобряете
msgid "Activity restored." msgid "Activity restored."
msgstr "Активность восстановлена." msgstr "Активность восстановлена."
#: lib/who_need_help_web/live/activity_live/index.ex:427 #: lib/who_need_help_web/live/activity_live/index.ex:491
#: lib/who_need_help_web/live/activity_live/new.ex:204 #: lib/who_need_help_web/live/activity_live/new.ex:204
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity type" msgid "Activity type"
@ -243,12 +243,12 @@ msgstr "Добавить неподтверждённую ссылку"
msgid "Administrator" msgid "Administrator"
msgstr "Администратор" msgstr "Администратор"
#: lib/who_need_help_web/live/activity_live/index.ex:429 #: lib/who_need_help_web/live/activity_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All activities" msgid "All activities"
msgstr "Все активности" msgstr "Все активности"
#: lib/who_need_help_web/live/request_live/index.ex:407 #: lib/who_need_help_web/live/request_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All categories" msgid "All categories"
msgstr "Все категории" msgstr "Все категории"
@ -263,7 +263,7 @@ msgstr "Уже зарегистрированы?"
msgid "Another helper already accepted this request." msgid "Another helper already accepted this request."
msgstr "Другой помощник уже принял эту заявку." msgstr "Другой помощник уже принял эту заявку."
#: lib/who_need_help_web/live/request_live/index.ex:422 #: lib/who_need_help_web/live/request_live/index.ex:508
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Any urgency" msgid "Any urgency"
msgstr "Любая срочность" msgstr "Любая срочность"
@ -383,16 +383,16 @@ msgstr "Отменить активность"
msgid "Cancel request" msgid "Cancel request"
msgstr "Отменить заявку" msgstr "Отменить заявку"
#: lib/who_need_help_web/live/activity_live/index.ex:358 #: lib/who_need_help_web/live/activity_live/index.ex:422
#: lib/who_need_help_web/live/activity_live/show.ex:344 #: lib/who_need_help_web/live/activity_live/show.ex:344
#: lib/who_need_help_web/live/request_live/index.ex:327 #: lib/who_need_help_web/live/request_live/index.ex:413
#: lib/who_need_help_web/live/request_live/index.ex:328 #: lib/who_need_help_web/live/request_live/index.ex:414
#: lib/who_need_help_web/live/request_live/show.ex:956 #: lib/who_need_help_web/live/request_live/show.ex:956
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancelled" msgid "Cancelled"
msgstr "Отменено" msgstr "Отменено"
#: lib/who_need_help_web/live/request_live/index.ex:405 #: lib/who_need_help_web/live/request_live/index.ex:491
#: lib/who_need_help_web/live/request_live/new.ex:302 #: lib/who_need_help_web/live/request_live/new.ex:302
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Category" msgid "Category"
@ -476,7 +476,7 @@ msgstr "Закрыть"
msgid "Coffee and conversation" msgid "Coffee and conversation"
msgstr "Кофе и разговоры" msgstr "Кофе и разговоры"
#: lib/who_need_help_web/live/activity_live/index.ex:372 #: lib/who_need_help_web/live/activity_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
msgstr "" msgstr ""
@ -493,19 +493,19 @@ msgstr "Комментарий (необязательно)"
msgid "Community helpers" msgid "Community helpers"
msgstr "Помощники сообщества" msgstr "Помощники сообщества"
#: lib/who_need_help_web/live/activity_live/index.ex:578 #: lib/who_need_help_web/live/activity_live/index.ex:647
#: lib/who_need_help_web/live/activity_live/show.ex:403 #: lib/who_need_help_web/live/activity_live/show.ex:403
#: lib/who_need_help_web/live/activity_live/show.ex:639 #: lib/who_need_help_web/live/activity_live/show.ex:639
#: lib/who_need_help_web/live/activity_live/show.ex:698 #: lib/who_need_help_web/live/activity_live/show.ex:698
#: lib/who_need_help_web/live/request_live/index.ex:565 #: lib/who_need_help_web/live/request_live/index.ex:656
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Community member" msgid "Community member"
msgstr "Член сообщества" msgstr "Член сообщества"
#: lib/who_need_help_web/live/activity_live/index.ex:357 #: lib/who_need_help_web/live/activity_live/index.ex:421
#: lib/who_need_help_web/live/activity_live/show.ex:343 #: lib/who_need_help_web/live/activity_live/show.ex:343
#: lib/who_need_help_web/live/request_live/index.ex:325 #: lib/who_need_help_web/live/request_live/index.ex:411
#: lib/who_need_help_web/live/request_live/index.ex:326 #: lib/who_need_help_web/live/request_live/index.ex:412
#: lib/who_need_help_web/live/request_live/show.ex:955 #: lib/who_need_help_web/live/request_live/show.ex:955
#: lib/who_need_help_web/live/request_live/show.ex:974 #: lib/who_need_help_web/live/request_live/show.ex:974
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -558,13 +558,13 @@ msgstr "Инструкции по подтверждению"
msgid "Confirming..." msgid "Confirming..."
msgstr "Подтверждение…" msgstr "Подтверждение…"
#: lib/who_need_help_web/live/activity_live/index.ex:378 #: lib/who_need_help_web/live/activity_live/index.ex:442
#: lib/who_need_help_web/live/activity_live/new.ex:12 #: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create activity" msgid "Create activity"
msgstr "Создать активность" msgstr "Создать активность"
#: lib/who_need_help_web/live/activity_live/index.ex:650 #: lib/who_need_help_web/live/activity_live/index.ex:722
#: lib/who_need_help_web/live/activity_live/new.ex:180 #: lib/who_need_help_web/live/activity_live/new.ex:180
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create an activity" msgid "Create an activity"
@ -575,7 +575,7 @@ msgstr "Создать активность"
msgid "Create category" msgid "Create category"
msgstr "Создать категорию" msgstr "Создать категорию"
#: lib/who_need_help_web/live/request_live/index.ex:356 #: lib/who_need_help_web/live/request_live/index.ex:442
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create urgent request" msgid "Create urgent request"
msgstr "Создать срочную заявку" msgstr "Создать срочную заявку"
@ -737,7 +737,7 @@ msgstr "Истекает:"
msgid "Explain what is already arranged and what the volunteer needs to do." msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "Объясните, что уже сделано и что нужно сделать волонтеру." msgstr "Объясните, что уже сделано и что нужно сделать волонтеру."
#: lib/who_need_help_web/live/activity_live/index.ex:369 #: lib/who_need_help_web/live/activity_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Find people to join" msgid "Find people to join"
msgstr "Найти компанию" msgstr "Найти компанию"
@ -938,8 +938,8 @@ msgstr ""
"Android показывает постоянное уведомление и может продолжать передачу после " "Android показывает постоянное уведомление и может продолжать передачу после "
"сворачивания. При остановке передачи исходные координаты удаляются." "сворачивания. При остановке передачи исходные координаты удаляются."
#: lib/who_need_help_web/live/request_live/index.ex:323 #: lib/who_need_help_web/live/request_live/index.ex:409
#: lib/who_need_help_web/live/request_live/index.ex:324 #: lib/who_need_help_web/live/request_live/index.ex:410
#: lib/who_need_help_web/live/request_live/show.ex:954 #: lib/who_need_help_web/live/request_live/show.ex:954
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "In progress" msgid "In progress"
@ -1069,8 +1069,8 @@ msgstr "Отметить завершённой"
msgid "Match and coordinate" msgid "Match and coordinate"
msgstr "Найдите помощника и согласуйте детали" msgstr "Найдите помощника и согласуйте детали"
#: lib/who_need_help_web/live/request_live/index.ex:321 #: lib/who_need_help_web/live/request_live/index.ex:407
#: lib/who_need_help_web/live/request_live/index.ex:322 #: lib/who_need_help_web/live/request_live/index.ex:408
#: lib/who_need_help_web/live/request_live/show.ex:952 #: lib/who_need_help_web/live/request_live/show.ex:952
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Matched" msgid "Matched"
@ -1099,7 +1099,7 @@ msgstr ""
msgid "Medicine requests" msgid "Medicine requests"
msgstr "Заявки на лекарства" msgstr "Заявки на лекарства"
#: lib/who_need_help_web/live/activity_live/index.ex:385 #: lib/who_need_help_web/live/activity_live/index.ex:449
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
msgstr "" msgstr ""
@ -1153,12 +1153,12 @@ msgstr "Модератор"
msgid "Moderator access is required." msgid "Moderator access is required."
msgstr "Требуется доступ модератора." msgstr "Требуется доступ модератора."
#: lib/who_need_help_web/live/request_live/index.ex:347 #: lib/who_need_help_web/live/request_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "NEARBY MUTUAL AID" msgid "NEARBY MUTUAL AID"
msgstr "ВЗАИМОПОМОЩЬ РЯДОМ" msgstr "ВЗАИМОПОМОЩЬ РЯДОМ"
#: lib/who_need_help_web/live/request_live/index.ex:34 #: lib/who_need_help_web/live/request_live/index.ex:47
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby help" msgid "Nearby help"
msgstr "Помощь рядом" msgstr "Помощь рядом"
@ -1241,9 +1241,9 @@ msgid "Not for emergencies"
msgstr "Не для чрезвычайных ситуаций" msgstr "Не для чрезвычайных ситуаций"
#: lib/who_need_help_web/live/notification_live.ex:748 #: lib/who_need_help_web/live/notification_live.ex:748
#: lib/who_need_help_web/live/request_live/index.ex:311 #: lib/who_need_help_web/live/request_live/index.ex:397
#: lib/who_need_help_web/live/request_live/index.ex:312 #: lib/who_need_help_web/live/request_live/index.ex:398
#: lib/who_need_help_web/live/request_live/index.ex:423 #: lib/who_need_help_web/live/request_live/index.ex:509
#: lib/who_need_help_web/live/request_live/new.ex:449 #: lib/who_need_help_web/live/request_live/new.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:943 #: lib/who_need_help_web/live/request_live/show.ex:943
#: lib/who_need_help_web/live/request_live/show.ex:944 #: lib/who_need_help_web/live/request_live/show.ex:944
@ -1251,7 +1251,7 @@ msgstr "Не для чрезвычайных ситуаций"
msgid "Now" msgid "Now"
msgstr "Сейчас" msgstr "Сейчас"
#: lib/who_need_help_web/live/activity_live/index.ex:367 #: lib/who_need_help_web/live/activity_live/index.ex:431
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OPTIONAL SOCIAL PLANS" msgid "OPTIONAL SOCIAL PLANS"
msgstr "НЕОБЯЗАТЕЛЬНЫЕ СОВМЕСТНЫЕ ПЛАНЫ" msgstr "НЕОБЯЗАТЕЛЬНЫЕ СОВМЕСТНЫЕ ПЛАНЫ"
@ -1271,12 +1271,12 @@ msgstr ""
#: lib/who_need_help/notifications/text.ex:140 #: lib/who_need_help/notifications/text.ex:140
#: lib/who_need_help_web/controllers/content_removal_html.ex:21 #: lib/who_need_help_web/controllers/content_removal_html.ex:21
#: lib/who_need_help_web/controllers/support_html.ex:16 #: lib/who_need_help_web/controllers/support_html.ex:16
#: lib/who_need_help_web/live/activity_live/index.ex:356 #: lib/who_need_help_web/live/activity_live/index.ex:420
#: lib/who_need_help_web/live/activity_live/show.ex:342 #: lib/who_need_help_web/live/activity_live/show.ex:342
#: lib/who_need_help_web/live/moderation_live.ex:258 #: lib/who_need_help_web/live/moderation_live.ex:258
#: lib/who_need_help_web/live/report_live.ex:104 #: lib/who_need_help_web/live/report_live.ex:104
#: lib/who_need_help_web/live/request_live/index.ex:319 #: lib/who_need_help_web/live/request_live/index.ex:405
#: lib/who_need_help_web/live/request_live/index.ex:320 #: lib/who_need_help_web/live/request_live/index.ex:406
#: lib/who_need_help_web/live/request_live/show.ex:951 #: lib/who_need_help_web/live/request_live/show.ex:951
#: lib/who_need_help_web/live/support_operations_live.ex:237 #: lib/who_need_help_web/live/support_operations_live.ex:237
#: lib/who_need_help_web/live/support_operations_live.ex:438 #: lib/who_need_help_web/live/support_operations_live.ex:438
@ -1748,9 +1748,9 @@ msgid "Save profile"
msgstr "Сохранить профиль" msgstr "Сохранить профиль"
#: lib/who_need_help_web/live/notification_live.ex:750 #: lib/who_need_help_web/live/notification_live.ex:750
#: lib/who_need_help_web/live/request_live/index.ex:315 #: lib/who_need_help_web/live/request_live/index.ex:401
#: lib/who_need_help_web/live/request_live/index.ex:316 #: lib/who_need_help_web/live/request_live/index.ex:402
#: lib/who_need_help_web/live/request_live/index.ex:425 #: lib/who_need_help_web/live/request_live/index.ex:511
#: lib/who_need_help_web/live/request_live/new.ex:451 #: lib/who_need_help_web/live/request_live/new.ex:451
#: lib/who_need_help_web/live/request_live/show.ex:947 #: lib/who_need_help_web/live/request_live/show.ex:947
#: lib/who_need_help_web/live/request_live/show.ex:948 #: lib/who_need_help_web/live/request_live/show.ex:948
@ -2022,7 +2022,7 @@ msgstr "Состояние этой активности уже изменило
msgid "This interaction is blocked." msgid "This interaction is blocked."
msgstr "Это взаимодействие заблокировано." msgstr "Это взаимодействие заблокировано."
#: lib/who_need_help_web/live/request_live/index.ex:363 #: lib/who_need_help_web/live/request_live/index.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:1022 #: lib/who_need_help_web/live/request_live/show.ex:1022
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgid "This is not an emergency service. Contact local emergency services for immediate danger."
@ -2075,9 +2075,9 @@ msgid "To see sent emails, visit"
msgstr "Чтобы посмотреть отправленные письма, откройте" msgstr "Чтобы посмотреть отправленные письма, откройте"
#: lib/who_need_help_web/live/notification_live.ex:749 #: lib/who_need_help_web/live/notification_live.ex:749
#: lib/who_need_help_web/live/request_live/index.ex:313 #: lib/who_need_help_web/live/request_live/index.ex:399
#: lib/who_need_help_web/live/request_live/index.ex:314 #: lib/who_need_help_web/live/request_live/index.ex:400
#: lib/who_need_help_web/live/request_live/index.ex:424 #: lib/who_need_help_web/live/request_live/index.ex:510
#: lib/who_need_help_web/live/request_live/new.ex:450 #: lib/who_need_help_web/live/request_live/new.ex:450
#: lib/who_need_help_web/live/request_live/show.ex:945 #: lib/who_need_help_web/live/request_live/show.ex:945
#: lib/who_need_help_web/live/request_live/show.ex:946 #: lib/who_need_help_web/live/request_live/show.ex:946
@ -2152,7 +2152,7 @@ msgid "Update email instructions"
msgstr "Инструкции для изменения электронной почты" msgstr "Инструкции для изменения электронной почты"
#: lib/who_need_help_web/live/notification_live.ex:743 #: lib/who_need_help_web/live/notification_live.ex:743
#: lib/who_need_help_web/live/request_live/index.ex:420 #: lib/who_need_help_web/live/request_live/index.ex:506
#: lib/who_need_help_web/live/request_live/new.ex:447 #: lib/who_need_help_web/live/request_live/new.ex:447
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Urgency" msgid "Urgency"
@ -2308,7 +2308,7 @@ msgstr ""
"через подписку ChatGPT разработчика. Ключ OpenAI API и резервный вариант API" "через подписку ChatGPT разработчика. Ключ OpenAI API и резервный вариант API"
" с оплатой по использованию не применяются." " с оплатой по использованию не применяются."
#: lib/who_need_help_web/live/request_live/index.ex:348 #: lib/who_need_help_web/live/request_live/index.ex:434
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Who needs help?" msgid "Who needs help?"
msgstr "Кому нужна помощь?" msgstr "Кому нужна помощь?"
@ -2432,7 +2432,7 @@ msgid "Your review is revealed only after both participants submit."
msgstr "" msgstr ""
"Ваш отзыв откроется только после того, как его отправят оба участника." "Ваш отзыв откроется только после того, как его отправят оба участника."
#: lib/who_need_help_web/live/request_live/index.ex:630 #: lib/who_need_help_web/live/request_live/index.ex:724
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Your trust summary" msgid "Your trust summary"
msgstr "Ваши показатели доверия" msgstr "Ваши показатели доверия"
@ -2453,17 +2453,17 @@ msgstr "сообщение активности"
msgid "before confirming." msgid "before confirming."
msgstr "перед подтверждением." msgstr "перед подтверждением."
#: lib/who_need_help_web/live/activity_live/index.ex:577 #: lib/who_need_help_web/live/activity_live/index.ex:646
#: lib/who_need_help_web/live/category_proposal_live.ex:252 #: lib/who_need_help_web/live/category_proposal_live.ex:252
#: lib/who_need_help_web/live/moderation_live.ex:372 #: lib/who_need_help_web/live/moderation_live.ex:372
#: lib/who_need_help_web/live/request_live/index.ex:564 #: lib/who_need_help_web/live/request_live/index.ex:655
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "by %{name}" msgid "by %{name}"
msgstr "автор: %{name}" msgstr "автор: %{name}"
#: lib/who_need_help_web/live/profile_live.ex:190 #: lib/who_need_help_web/live/profile_live.ex:190
#: lib/who_need_help_web/live/public_profile_live.ex:133 #: lib/who_need_help_web/live/public_profile_live.ex:133
#: lib/who_need_help_web/live/request_live/index.ex:634 #: lib/who_need_help_web/live/request_live/index.ex:728
#: lib/who_need_help_web/live/request_live/show.ex:1374 #: lib/who_need_help_web/live/request_live/show.ex:1374
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "completed" msgid "completed"
@ -2499,7 +2499,7 @@ msgstr "организатор"
msgid "participants only" msgid "participants only"
msgstr "только участники" msgstr "только участники"
#: lib/who_need_help_web/live/request_live/index.ex:638 #: lib/who_need_help_web/live/request_live/index.ex:732
#: lib/who_need_help_web/live/request_live/show.ex:1378 #: lib/who_need_help_web/live/request_live/show.ex:1378
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "people" msgid "people"
@ -2559,7 +2559,7 @@ msgstr "срочно"
#: lib/who_need_help_web/live/activity_live/show.ex:719 #: lib/who_need_help_web/live/activity_live/show.ex:719
#: lib/who_need_help_web/live/profile_live.ex:285 #: lib/who_need_help_web/live/profile_live.ex:285
#: lib/who_need_help_web/live/public_profile_live.ex:98 #: lib/who_need_help_web/live/public_profile_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:642 #: lib/who_need_help_web/live/request_live/index.ex:736
#: lib/who_need_help_web/live/request_live/show.ex:1093 #: lib/who_need_help_web/live/request_live/show.ex:1093
#: lib/who_need_help_web/live/request_live/show.ex:1476 #: lib/who_need_help_web/live/request_live/show.ex:1476
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2592,8 +2592,8 @@ msgstr "← Заявки"
msgid "Approved" msgid "Approved"
msgstr "Одобрено" msgstr "Одобрено"
#: lib/who_need_help_web/live/request_live/index.ex:329 #: lib/who_need_help_web/live/request_live/index.ex:415
#: lib/who_need_help_web/live/request_live/index.ex:330 #: lib/who_need_help_web/live/request_live/index.ex:416
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expired" msgid "Expired"
msgstr "Истекло" msgstr "Истекло"
@ -2646,8 +2646,8 @@ msgstr "Необычная частота действий"
msgid "Load earlier messages" msgid "Load earlier messages"
msgstr "Загрузить предыдущие сообщения" msgstr "Загрузить предыдущие сообщения"
#: lib/who_need_help_web/live/activity_live/index.ex:589 #: lib/who_need_help_web/live/activity_live/index.ex:658
#: lib/who_need_help_web/live/activity_live/index.ex:679 #: lib/who_need_help_web/live/activity_live/index.ex:751
#: lib/who_need_help_web/live/category_proposal_live.ex:284 #: lib/who_need_help_web/live/category_proposal_live.ex:284
#: lib/who_need_help_web/live/leaderboard_live.ex:91 #: lib/who_need_help_web/live/leaderboard_live.ex:91
#: lib/who_need_help_web/live/moderation_live.ex:465 #: lib/who_need_help_web/live/moderation_live.ex:465
@ -2659,7 +2659,7 @@ msgstr "Загрузить предыдущие сообщения"
#: lib/who_need_help_web/live/profile_live.ex:363 #: lib/who_need_help_web/live/profile_live.ex:363
#: lib/who_need_help_web/live/public_profile_live.ex:178 #: lib/who_need_help_web/live/public_profile_live.ex:178
#: lib/who_need_help_web/live/report_live.ex:98 #: lib/who_need_help_web/live/report_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:699 #: lib/who_need_help_web/live/request_live/index.ex:805
#: lib/who_need_help_web/live/support_operations_live.ex:306 #: lib/who_need_help_web/live/support_operations_live.ex:306
#: lib/who_need_help_web/live/support_operations_live.ex:505 #: lib/who_need_help_web/live/support_operations_live.ex:505
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2667,7 +2667,7 @@ msgid "Load more"
msgstr "Загрузить ещё" msgstr "Загрузить ещё"
#: lib/who_need_help_web/live/activity_live/show.ex:544 #: lib/who_need_help_web/live/activity_live/show.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:610 #: lib/who_need_help_web/live/request_live/index.ex:701
#: lib/who_need_help_web/live/request_live/show.ex:1275 #: lib/who_need_help_web/live/request_live/show.ex:1275
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable." msgid "The map is unavailable in this browser. Request details remain usable."
@ -4656,7 +4656,7 @@ msgstr "Перетащите, чтобы переместить точную т
msgid "Exact point selected. Drag the red pin or click the map to move it." msgid "Exact point selected. Drag the red pin or click the map to move it."
msgstr "Точная точка выбрана. Перетащите красный маркер или нажмите на карту, чтобы переместить её." msgstr "Точная точка выбрана. Перетащите красный маркер или нажмите на карту, чтобы переместить её."
#: lib/who_need_help_web/live/request_live/index.ex:453 #: lib/who_need_help_web/live/request_live/index.ex:539
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} request in this map area" msgid "%{count} request in this map area"
msgid_plural "%{count} requests in this map area" msgid_plural "%{count} requests in this map area"
@ -4664,37 +4664,37 @@ msgstr[0] "%{count} заявка в этой области карты"
msgstr[1] "%{count} заявки в этой области карты" msgstr[1] "%{count} заявки в этой области карты"
msgstr[2] "%{count} заявок в этой области карты" msgstr[2] "%{count} заявок в этой области карты"
#: lib/who_need_help_web/live/activity_live/index.ex:438 #: lib/who_need_help_web/live/activity_live/index.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:430 #: lib/who_need_help_web/live/request_live/index.ex:516
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Area or city" msgid "Area or city"
msgstr "Район или город" msgstr "Район или город"
#: lib/who_need_help_web/live/activity_live/index.ex:525 #: lib/who_need_help_web/live/activity_live/index.ex:594
#: lib/who_need_help_web/live/request_live/index.ex:515 #: lib/who_need_help_web/live/request_live/index.ex:606
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Choose an area on the map" msgid "Choose an area on the map"
msgstr "Выберите область на карте" msgstr "Выберите область на карте"
#: lib/who_need_help_web/live/request_live/index.ex:350 #: lib/who_need_help_web/live/request_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters." msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters."
msgstr "Просматривайте безопасные для приватности области и точные публичные места встречи, выбранные авторами заявок." msgstr "Просматривайте безопасные для приватности области и точные публичные места встречи, выбранные авторами заявок."
#: lib/who_need_help_web/live/activity_live/index.ex:484 #: lib/who_need_help_web/live/activity_live/index.ex:553
#: lib/who_need_help_web/live/request_live/index.ex:474 #: lib/who_need_help_web/live/request_live/index.ex:565
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "List" msgid "List"
msgstr "Список" msgstr "Список"
#: lib/who_need_help_web/live/request_live/index.ex:586 #: lib/who_need_help_web/live/request_live/index.ex:677
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Load more in this area" msgid "Load more in this area"
msgstr "Загрузить ещё в этой области" msgstr "Загрузить ещё в этой области"
#: lib/who_need_help_web/components/layouts/root.html.heex:5 #: lib/who_need_help_web/components/layouts/root.html.heex:5
#: lib/who_need_help_web/live/activity_live/index.ex:504 #: lib/who_need_help_web/live/activity_live/index.ex:573
#: lib/who_need_help_web/live/request_live/index.ex:494 #: lib/who_need_help_web/live/request_live/index.ex:585
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Map" msgid "Map"
msgstr "Карта" msgstr "Карта"
@ -4704,53 +4704,53 @@ msgstr "Карта"
msgid "Map preview" msgid "Map preview"
msgstr "Предпросмотр карты" msgstr "Предпросмотр карты"
#: lib/who_need_help_web/live/activity_live/index.ex:537 #: lib/who_need_help_web/live/activity_live/index.ex:606
#: lib/who_need_help_web/live/request_live/index.ex:527 #: lib/who_need_help_web/live/request_live/index.ex:618
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move or zoom the map to search somewhere else." msgid "Move or zoom the map to search somewhere else."
msgstr "Переместите или масштабируйте карту, чтобы искать в другом месте." msgstr "Переместите или масштабируйте карту, чтобы искать в другом месте."
#: lib/who_need_help_web/live/activity_live/index.ex:471 #: lib/who_need_help_web/live/activity_live/index.ex:540
#: lib/who_need_help_web/live/request_live/index.ex:461 #: lib/who_need_help_web/live/request_live/index.ex:552
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move the map, then search the newly visible area." msgid "Move the map, then search the newly visible area."
msgstr "Переместите карту, затем выполните поиск в новой видимой области." msgstr "Переместите карту, затем выполните поиск в новой видимой области."
#: lib/who_need_help_web/live/request_live/index.ex:525 #: lib/who_need_help_web/live/request_live/index.ex:616
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open requests in this area" msgid "No open requests in this area"
msgstr "В этой области нет открытых заявок" msgstr "В этой области нет открытых заявок"
#: lib/who_need_help_web/live/request_live/index.ex:517 #: lib/who_need_help_web/live/request_live/index.ex:608
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only requests from the selected map area will be loaded." msgid "Only requests from the selected map area will be loaded."
msgstr "Будут загружены только заявки из выбранной области карты." msgstr "Будут загружены только заявки из выбранной области карты."
#: lib/who_need_help_web/components/layouts/root.html.heex:8 #: lib/who_need_help_web/components/layouts/root.html.heex:8
#: lib/who_need_help_web/live/request_live/index.ex:613 #: lib/who_need_help_web/live/request_live/index.ex:704
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open request" msgid "Open request"
msgstr "Открыть заявку" msgstr "Открыть заявку"
#: lib/who_need_help_web/live/activity_live/index.ex:439 #: lib/who_need_help_web/live/activity_live/index.ex:503
#: lib/who_need_help_web/live/request_live/index.ex:431 #: lib/who_need_help_web/live/request_live/index.ex:517
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Optional text search" msgid "Optional text search"
msgstr "Необязательный текстовый поиск" msgstr "Необязательный текстовый поиск"
#: lib/who_need_help_web/live/request_live/index.ex:592 #: lib/who_need_help_web/live/request_live/index.ex:683
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests without map coordinates appear only when their public area description matches the text search." msgid "Requests without map coordinates appear only when their public area description matches the text search."
msgstr "Заявки без координат на карте отображаются только тогда, когда описание публичной области совпадает с текстовым поиском." msgstr "Заявки без координат на карте отображаются только тогда, когда описание публичной области совпадает с текстовым поиском."
#: lib/who_need_help_web/live/activity_live/index.ex:475 #: lib/who_need_help_web/live/activity_live/index.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:465 #: lib/who_need_help_web/live/request_live/index.ex:556
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Results layout" msgid "Results layout"
msgstr "Расположение результатов" msgstr "Расположение результатов"
#: lib/who_need_help_web/live/activity_live/index.ex:621 #: lib/who_need_help_web/live/activity_live/index.ex:693
#: lib/who_need_help_web/live/request_live/index.ex:625 #: lib/who_need_help_web/live/request_live/index.ex:719
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search this area" msgid "Search this area"
msgstr "Искать в этой области" msgstr "Искать в этой области"
@ -4760,21 +4760,21 @@ msgstr "Искать в этой области"
msgid "Static preview" msgid "Static preview"
msgstr "Статический просмотр" msgstr "Статический просмотр"
#: lib/who_need_help_web/live/activity_live/index.ex:494 #: lib/who_need_help_web/live/activity_live/index.ex:563
#: lib/who_need_help_web/live/request_live/index.ex:484 #: lib/who_need_help_web/live/request_live/index.ex:575
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Together" msgid "Together"
msgstr "Вместе" msgstr "Вместе"
#: lib/who_need_help_web/components/layouts/root.html.heex:6 #: lib/who_need_help_web/components/layouts/root.html.heex:6
#: lib/who_need_help_web/controllers/page_html/home.html.heex:160 #: lib/who_need_help_web/controllers/page_html/home.html.heex:160
#: lib/who_need_help_web/live/activity_live/index.ex:609 #: lib/who_need_help_web/live/activity_live/index.ex:681
#: lib/who_need_help_web/live/activity_live/new.ex:355 #: lib/who_need_help_web/live/activity_live/new.ex:355
#: lib/who_need_help_web/live/activity_live/new.ex:414 #: lib/who_need_help_web/live/activity_live/new.ex:414
#: lib/who_need_help_web/live/activity_live/show.ex:546 #: lib/who_need_help_web/live/activity_live/show.ex:546
#: lib/who_need_help_web/live/notification_live.ex:602 #: lib/who_need_help_web/live/notification_live.ex:602
#: lib/who_need_help_web/live/notification_live.ex:621 #: lib/who_need_help_web/live/notification_live.ex:621
#: lib/who_need_help_web/live/request_live/index.ex:612 #: lib/who_need_help_web/live/request_live/index.ex:703
#: lib/who_need_help_web/live/request_live/new.ex:485 #: lib/who_need_help_web/live/request_live/new.ex:485
#: lib/who_need_help_web/live/request_live/new.ex:563 #: lib/who_need_help_web/live/request_live/new.ex:563
#: lib/who_need_help_web/live/request_live/show.ex:1277 #: lib/who_need_help_web/live/request_live/show.ex:1277
@ -5164,7 +5164,7 @@ msgid "Nearby alert deleted."
msgstr "Уведомление поблизости удалено." msgstr "Уведомление поблизости удалено."
#: lib/who_need_help_web/live/notification_live.ex:456 #: lib/who_need_help_web/live/notification_live.ex:456
#: lib/who_need_help_web/live/request_live/index.ex:376 #: lib/who_need_help_web/live/request_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby requests" msgid "Nearby requests"
msgstr "Заявки поблизости" msgstr "Заявки поблизости"
@ -5504,7 +5504,7 @@ msgid "WebGL is unavailable in this browser, so the full map cannot start. Selec
msgstr "WebGL недоступен в этом браузере, поэтому полная карта не может запуститься. Выберите пример ниже или попробуйте запустить карту ещё раз." msgstr "WebGL недоступен в этом браузере, поэтому полная карта не может запуститься. Выберите пример ниже или попробуйте запустить карту ещё раз."
#: lib/who_need_help_web/components/layouts/root.html.heex:7 #: lib/who_need_help_web/components/layouts/root.html.heex:7
#: lib/who_need_help_web/live/activity_live/index.ex:610 #: lib/who_need_help_web/live/activity_live/index.ex:682
#: lib/who_need_help_web/live/activity_live/show.ex:547 #: lib/who_need_help_web/live/activity_live/show.ex:547
#: lib/who_need_help_web/live/request_live/show.ex:1278 #: lib/who_need_help_web/live/request_live/show.ex:1278
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -6069,7 +6069,7 @@ msgstr "Теперь ваш отзыв виден"
msgid "Delivery settings" msgid "Delivery settings"
msgstr "Настройки доставки" msgstr "Настройки доставки"
#: lib/who_need_help_web/live/request_live/index.ex:569 #: lib/who_need_help_web/live/request_live/index.ex:660
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expires" msgid "Expires"
msgstr "Истекает" msgstr "Истекает"
@ -6089,12 +6089,12 @@ msgstr "Оповещения рядом"
msgid "Notification sections" msgid "Notification sections"
msgstr "Разделы уведомлений" msgstr "Разделы уведомлений"
#: lib/who_need_help_web/live/activity_live/index.ex:605 #: lib/who_need_help_web/live/activity_live/index.ex:674
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activities" msgid "%{count} activities"
msgstr "Активностей: %{count}" msgstr "Активностей: %{count}"
#: lib/who_need_help_web/live/activity_live/index.ex:463 #: lib/who_need_help_web/live/activity_live/index.ex:527
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activity in this map area" msgid "%{count} activity in this map area"
msgid_plural "%{count} activities in this map area" msgid_plural "%{count} activities in this map area"
@ -6102,17 +6102,17 @@ msgstr[0] "%{count} активность в этой области карты"
msgstr[1] "%{count} активности в этой области карты" msgstr[1] "%{count} активности в этой области карты"
msgstr[2] "%{count} активностей в этой области карты" msgstr[2] "%{count} активностей в этой области карты"
#: lib/who_need_help_web/live/request_live/index.ex:614 #: lib/who_need_help_web/live/request_live/index.ex:705
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} requests" msgid "%{count} requests"
msgstr "Заявок: %{count}" msgstr "Заявок: %{count}"
#: lib/who_need_help_web/live/activity_live/index.ex:645 #: lib/who_need_help_web/live/activity_live/index.ex:717
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list." msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list."
msgstr "Созданные вами активности и те, к которым вы попросили присоединиться, появятся здесь. После выхода активность исчезнет из списка." msgstr "Созданные вами активности и те, к которым вы попросили присоединиться, появятся здесь. После выхода активность исчезнет из списка."
#: lib/who_need_help_web/live/activity_live/index.ex:393 #: lib/who_need_help_web/live/activity_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity views" msgid "Activity views"
msgstr "Разделы активностей" msgstr "Разделы активностей"
@ -6127,12 +6127,12 @@ msgstr "Выберите категорию и понятно опишите з
msgid "Confirm the safety guidance before asking nearby people for help." msgid "Confirm the safety guidance before asking nearby people for help."
msgstr "Подтвердите правила безопасности перед публикацией заявки." msgstr "Подтвердите правила безопасности перед публикацией заявки."
#: lib/who_need_help_web/live/request_live/index.ex:659 #: lib/who_need_help_web/live/request_live/index.ex:753
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create request" msgid "Create request"
msgstr "Создать заявку" msgstr "Создать заявку"
#: lib/who_need_help_web/live/request_live/index.ex:673 #: lib/who_need_help_web/live/request_live/index.ex:776
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create your first request" msgid "Create your first request"
msgstr "Создать первую заявку" msgstr "Создать первую заявку"
@ -6147,43 +6147,43 @@ msgstr "Что нужно сделать"
msgid "Location" msgid "Location"
msgstr "Место" msgstr "Место"
#: lib/who_need_help_web/live/activity_live/index.ex:399 #: lib/who_need_help_web/live/activity_live/index.ex:463
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My activities" msgid "My activities"
msgstr "Мои активности" msgstr "Мои активности"
#: lib/who_need_help_web/live/request_live/index.ex:377 #: lib/who_need_help_web/live/request_live/index.ex:463
#: lib/who_need_help_web/live/request_live/index.ex:653 #: lib/who_need_help_web/live/request_live/index.ex:747
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My requests" msgid "My requests"
msgstr "Мои заявки" msgstr "Мои заявки"
#: lib/who_need_help_web/live/activity_live/index.ex:398 #: lib/who_need_help_web/live/activity_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby activities" msgid "Nearby activities"
msgstr "Активности рядом" msgstr "Активности рядом"
#: lib/who_need_help_web/live/activity_live/index.ex:643 #: lib/who_need_help_web/live/activity_live/index.ex:715
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No activities yet" msgid "No activities yet"
msgstr "Активностей пока нет" msgstr "Активностей пока нет"
#: lib/who_need_help_web/live/activity_live/index.ex:535 #: lib/who_need_help_web/live/activity_live/index.ex:604
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open activities in this area" msgid "No open activities in this area"
msgstr "В этой области нет открытых активностей" msgstr "В этой области нет открытых активностей"
#: lib/who_need_help_web/live/request_live/index.ex:668 #: lib/who_need_help_web/live/request_live/index.ex:771
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No requests yet" msgid "No requests yet"
msgstr "Заявок пока нет" msgstr "Заявок пока нет"
#: lib/who_need_help_web/live/activity_live/index.ex:527 #: lib/who_need_help_web/live/activity_live/index.ex:596
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only activities from the selected map area will be loaded." msgid "Only activities from the selected map area will be loaded."
msgstr "Будут загружены только активности из выбранной области карты." msgstr "Будут загружены только активности из выбранной области карты."
#: lib/who_need_help_web/live/activity_live/index.ex:604 #: lib/who_need_help_web/live/activity_live/index.ex:673
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open activity" msgid "Open activity"
msgstr "Открыть активность" msgstr "Открыть активность"
@ -6193,12 +6193,12 @@ msgstr "Открыть активность"
msgid "Request form steps" msgid "Request form steps"
msgstr "Шаги создания заявки" msgstr "Шаги создания заявки"
#: lib/who_need_help_web/live/request_live/index.ex:371 #: lib/who_need_help_web/live/request_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Request views" msgid "Request views"
msgstr "Разделы заявок" msgstr "Разделы заявок"
#: lib/who_need_help_web/live/request_live/index.ex:670 #: lib/who_need_help_web/live/request_live/index.ex:773
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests you publish or accept will appear here." msgid "Requests you publish or accept will appear here."
msgstr "Здесь появятся заявки, которые вы создали или приняли." msgstr "Здесь появятся заявки, которые вы создали или приняли."
@ -6223,7 +6223,7 @@ msgstr "Укажите срочность и время закрытия зая
msgid "Share only as much location detail as helpers need." msgid "Share only as much location detail as helpers need."
msgstr "Покажите помощникам только необходимую информацию о месте." msgstr "Покажите помощникам только необходимую информацию о месте."
#: lib/who_need_help_web/live/activity_live/index.ex:607 #: lib/who_need_help_web/live/activity_live/index.ex:679
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Activity details remain usable." msgid "The map is unavailable in this browser. Activity details remain usable."
msgstr "Карта недоступна в этом браузере. Информация об активностях по-прежнему доступна." msgstr "Карта недоступна в этом браузере. Информация об активностях по-прежнему доступна."
@ -6233,7 +6233,7 @@ msgstr "Карта недоступна в этом браузере. Инфор
msgid "Timing" msgid "Timing"
msgstr "Время" msgstr "Время"
#: lib/who_need_help_web/live/request_live/index.ex:655 #: lib/who_need_help_web/live/request_live/index.ex:749
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Track requests you created and requests you accepted as a helper." msgid "Track requests you created and requests you accepted as a helper."
msgstr "Следите за созданными заявками и за теми, которые вы приняли как помощник." msgstr "Следите за созданными заявками и за теми, которые вы приняли как помощник."
@ -6894,11 +6894,32 @@ msgstr "Что должны видеть люди до одобрения?"
msgid "Choose the meeting point on the map or use your current location." msgid "Choose the meeting point on the map or use your current location."
msgstr "Выберите место встречи на карте или используйте своё текущее местоположение." msgstr "Выберите место встречи на карте или используйте своё текущее местоположение."
#: lib/who_need_help_web/live/activity_live/index.ex:458 #: lib/who_need_help_web/live/activity_live/index.ex:522
#: lib/who_need_help_web/live/activity_live/index.ex:517 #: lib/who_need_help_web/live/activity_live/index.ex:586
#: lib/who_need_help_web/live/activity_live/index.ex:635 #: lib/who_need_help_web/live/activity_live/index.ex:707
#: lib/who_need_help_web/live/request_live/index.ex:448 #: lib/who_need_help_web/live/request_live/index.ex:534
#: lib/who_need_help_web/live/request_live/index.ex:507 #: lib/who_need_help_web/live/request_live/index.ex:598
#: lib/who_need_help_web/live/request_live/index.ex:763
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Loading results…" msgid "Loading results…"
msgstr "Загружаем результаты…" msgstr "Загружаем результаты…"
#: lib/who_need_help_web/live/activity_live/index.ex:676
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching activities are listed beside the map."
msgstr "Достигнут максимальный масштаб. Подходящие мероприятия перечислены рядом с картой."
#: lib/who_need_help_web/live/request_live/index.ex:707
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching requests are listed beside the map."
msgstr "Достигнут максимальный масштаб. Подходящие заявки перечислены рядом с картой."
#: lib/who_need_help_web/live/activity_live/index.ex:535
#, elixir-autogen, elixir-format
msgid "of %{count} matching activities with map locations"
msgstr "из %{count} подходящих мероприятий с местоположением на карте"
#: lib/who_need_help_web/live/request_live/index.ex:547
#, elixir-autogen, elixir-format
msgid "of %{count} matching requests with map locations"
msgstr "из %{count} подходящих заявок с местоположением на карте"

View File

@ -184,7 +184,7 @@ msgid "Active"
msgstr "Активний" msgstr "Активний"
#: lib/who_need_help_web/components/layouts.ex:231 #: lib/who_need_help_web/components/layouts.ex:231
#: lib/who_need_help_web/live/activity_live/index.ex:33 #: lib/who_need_help_web/live/activity_live/index.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities" msgid "Activities"
msgstr "Активності" msgstr "Активності"
@ -226,7 +226,7 @@ msgstr "Активність опубліковано. Ви схвалюєте
msgid "Activity restored." msgid "Activity restored."
msgstr "Активність відновлено." msgstr "Активність відновлено."
#: lib/who_need_help_web/live/activity_live/index.ex:427 #: lib/who_need_help_web/live/activity_live/index.ex:491
#: lib/who_need_help_web/live/activity_live/new.ex:204 #: lib/who_need_help_web/live/activity_live/new.ex:204
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity type" msgid "Activity type"
@ -243,12 +243,12 @@ msgstr "Додати непідтверджене посилання"
msgid "Administrator" msgid "Administrator"
msgstr "Адміністратор" msgstr "Адміністратор"
#: lib/who_need_help_web/live/activity_live/index.ex:429 #: lib/who_need_help_web/live/activity_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All activities" msgid "All activities"
msgstr "Усі активності" msgstr "Усі активності"
#: lib/who_need_help_web/live/request_live/index.ex:407 #: lib/who_need_help_web/live/request_live/index.ex:493
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "All categories" msgid "All categories"
msgstr "Усі категорії" msgstr "Усі категорії"
@ -263,7 +263,7 @@ msgstr "Уже зареєстровані?"
msgid "Another helper already accepted this request." msgid "Another helper already accepted this request."
msgstr "Інший помічник уже прийняв цю заявку." msgstr "Інший помічник уже прийняв цю заявку."
#: lib/who_need_help_web/live/request_live/index.ex:422 #: lib/who_need_help_web/live/request_live/index.ex:508
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Any urgency" msgid "Any urgency"
msgstr "Будь-яка терміновість" msgstr "Будь-яка терміновість"
@ -383,16 +383,16 @@ msgstr "Скасувати активність"
msgid "Cancel request" msgid "Cancel request"
msgstr "Скасувати запит" msgstr "Скасувати запит"
#: lib/who_need_help_web/live/activity_live/index.ex:358 #: lib/who_need_help_web/live/activity_live/index.ex:422
#: lib/who_need_help_web/live/activity_live/show.ex:344 #: lib/who_need_help_web/live/activity_live/show.ex:344
#: lib/who_need_help_web/live/request_live/index.ex:327 #: lib/who_need_help_web/live/request_live/index.ex:413
#: lib/who_need_help_web/live/request_live/index.ex:328 #: lib/who_need_help_web/live/request_live/index.ex:414
#: lib/who_need_help_web/live/request_live/show.ex:956 #: lib/who_need_help_web/live/request_live/show.ex:956
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancelled" msgid "Cancelled"
msgstr "Скасовано" msgstr "Скасовано"
#: lib/who_need_help_web/live/request_live/index.ex:405 #: lib/who_need_help_web/live/request_live/index.ex:491
#: lib/who_need_help_web/live/request_live/new.ex:302 #: lib/who_need_help_web/live/request_live/new.ex:302
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Category" msgid "Category"
@ -476,7 +476,7 @@ msgstr "Закрити"
msgid "Coffee and conversation" msgid "Coffee and conversation"
msgstr "Кава й розмова" msgstr "Кава й розмова"
#: lib/who_need_help_web/live/activity_live/index.ex:372 #: lib/who_need_help_web/live/activity_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings." msgid "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
msgstr "" msgstr ""
@ -493,19 +493,19 @@ msgstr "Коментар (необов’язково)"
msgid "Community helpers" msgid "Community helpers"
msgstr "Помічники спільноти" msgstr "Помічники спільноти"
#: lib/who_need_help_web/live/activity_live/index.ex:578 #: lib/who_need_help_web/live/activity_live/index.ex:647
#: lib/who_need_help_web/live/activity_live/show.ex:403 #: lib/who_need_help_web/live/activity_live/show.ex:403
#: lib/who_need_help_web/live/activity_live/show.ex:639 #: lib/who_need_help_web/live/activity_live/show.ex:639
#: lib/who_need_help_web/live/activity_live/show.ex:698 #: lib/who_need_help_web/live/activity_live/show.ex:698
#: lib/who_need_help_web/live/request_live/index.ex:565 #: lib/who_need_help_web/live/request_live/index.ex:656
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Community member" msgid "Community member"
msgstr "Член спільноти" msgstr "Член спільноти"
#: lib/who_need_help_web/live/activity_live/index.ex:357 #: lib/who_need_help_web/live/activity_live/index.ex:421
#: lib/who_need_help_web/live/activity_live/show.ex:343 #: lib/who_need_help_web/live/activity_live/show.ex:343
#: lib/who_need_help_web/live/request_live/index.ex:325 #: lib/who_need_help_web/live/request_live/index.ex:411
#: lib/who_need_help_web/live/request_live/index.ex:326 #: lib/who_need_help_web/live/request_live/index.ex:412
#: lib/who_need_help_web/live/request_live/show.ex:955 #: lib/who_need_help_web/live/request_live/show.ex:955
#: lib/who_need_help_web/live/request_live/show.ex:974 #: lib/who_need_help_web/live/request_live/show.ex:974
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -559,13 +559,13 @@ msgstr "Інструкції для підтвердження"
msgid "Confirming..." msgid "Confirming..."
msgstr "Підтвердження…" msgstr "Підтвердження…"
#: lib/who_need_help_web/live/activity_live/index.ex:378 #: lib/who_need_help_web/live/activity_live/index.ex:442
#: lib/who_need_help_web/live/activity_live/new.ex:12 #: lib/who_need_help_web/live/activity_live/new.ex:12
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create activity" msgid "Create activity"
msgstr "Створити активність" msgstr "Створити активність"
#: lib/who_need_help_web/live/activity_live/index.ex:650 #: lib/who_need_help_web/live/activity_live/index.ex:722
#: lib/who_need_help_web/live/activity_live/new.ex:180 #: lib/who_need_help_web/live/activity_live/new.ex:180
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create an activity" msgid "Create an activity"
@ -576,7 +576,7 @@ msgstr "Створити активність"
msgid "Create category" msgid "Create category"
msgstr "Створити категорію" msgstr "Створити категорію"
#: lib/who_need_help_web/live/request_live/index.ex:356 #: lib/who_need_help_web/live/request_live/index.ex:442
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create urgent request" msgid "Create urgent request"
msgstr "Створити термінову заявку" msgstr "Створити термінову заявку"
@ -736,7 +736,7 @@ msgstr "Спливає:"
msgid "Explain what is already arranged and what the volunteer needs to do." msgid "Explain what is already arranged and what the volunteer needs to do."
msgstr "Поясніть, що вже організовано й що має зробити волонтер." msgstr "Поясніть, що вже організовано й що має зробити волонтер."
#: lib/who_need_help_web/live/activity_live/index.ex:369 #: lib/who_need_help_web/live/activity_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Find people to join" msgid "Find people to join"
msgstr "Знайти компанію" msgstr "Знайти компанію"
@ -936,8 +936,8 @@ msgstr ""
"Android показує постійне сповіщення й може продовжувати передавання після " "Android показує постійне сповіщення й може продовжувати передавання після "
"згортання. Після зупинення передавання вихідні координати видаляються." "згортання. Після зупинення передавання вихідні координати видаляються."
#: lib/who_need_help_web/live/request_live/index.ex:323 #: lib/who_need_help_web/live/request_live/index.ex:409
#: lib/who_need_help_web/live/request_live/index.ex:324 #: lib/who_need_help_web/live/request_live/index.ex:410
#: lib/who_need_help_web/live/request_live/show.ex:954 #: lib/who_need_help_web/live/request_live/show.ex:954
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "In progress" msgid "In progress"
@ -1065,8 +1065,8 @@ msgstr "Позначити завершеною"
msgid "Match and coordinate" msgid "Match and coordinate"
msgstr "Знайдіть помічника й узгодьте деталі" msgstr "Знайдіть помічника й узгодьте деталі"
#: lib/who_need_help_web/live/request_live/index.ex:321 #: lib/who_need_help_web/live/request_live/index.ex:407
#: lib/who_need_help_web/live/request_live/index.ex:322 #: lib/who_need_help_web/live/request_live/index.ex:408
#: lib/who_need_help_web/live/request_live/show.ex:952 #: lib/who_need_help_web/live/request_live/show.ex:952
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Matched" msgid "Matched"
@ -1095,7 +1095,7 @@ msgstr ""
msgid "Medicine requests" msgid "Medicine requests"
msgstr "Запити на ліки" msgstr "Запити на ліки"
#: lib/who_need_help_web/live/activity_live/index.ex:385 #: lib/who_need_help_web/live/activity_live/index.ex:449
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe." msgid "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
msgstr "" msgstr ""
@ -1149,12 +1149,12 @@ msgstr "Модератор"
msgid "Moderator access is required." msgid "Moderator access is required."
msgstr "Потрібен доступ модератора." msgstr "Потрібен доступ модератора."
#: lib/who_need_help_web/live/request_live/index.ex:347 #: lib/who_need_help_web/live/request_live/index.ex:433
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "NEARBY MUTUAL AID" msgid "NEARBY MUTUAL AID"
msgstr "ВЗАЄМОДОПОМОГА ПОРУЧ" msgstr "ВЗАЄМОДОПОМОГА ПОРУЧ"
#: lib/who_need_help_web/live/request_live/index.ex:34 #: lib/who_need_help_web/live/request_live/index.ex:47
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby help" msgid "Nearby help"
msgstr "Допомога поруч" msgstr "Допомога поруч"
@ -1237,9 +1237,9 @@ msgid "Not for emergencies"
msgstr "Не для екстрених ситуацій" msgstr "Не для екстрених ситуацій"
#: lib/who_need_help_web/live/notification_live.ex:748 #: lib/who_need_help_web/live/notification_live.ex:748
#: lib/who_need_help_web/live/request_live/index.ex:311 #: lib/who_need_help_web/live/request_live/index.ex:397
#: lib/who_need_help_web/live/request_live/index.ex:312 #: lib/who_need_help_web/live/request_live/index.ex:398
#: lib/who_need_help_web/live/request_live/index.ex:423 #: lib/who_need_help_web/live/request_live/index.ex:509
#: lib/who_need_help_web/live/request_live/new.ex:449 #: lib/who_need_help_web/live/request_live/new.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:943 #: lib/who_need_help_web/live/request_live/show.ex:943
#: lib/who_need_help_web/live/request_live/show.ex:944 #: lib/who_need_help_web/live/request_live/show.ex:944
@ -1247,7 +1247,7 @@ msgstr "Не для екстрених ситуацій"
msgid "Now" msgid "Now"
msgstr "Зараз" msgstr "Зараз"
#: lib/who_need_help_web/live/activity_live/index.ex:367 #: lib/who_need_help_web/live/activity_live/index.ex:431
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OPTIONAL SOCIAL PLANS" msgid "OPTIONAL SOCIAL PLANS"
msgstr "НЕОБОВ’ЯЗКОВІ СПІЛЬНІ ПЛАНИ" msgstr "НЕОБОВ’ЯЗКОВІ СПІЛЬНІ ПЛАНИ"
@ -1267,12 +1267,12 @@ msgstr ""
#: lib/who_need_help/notifications/text.ex:140 #: lib/who_need_help/notifications/text.ex:140
#: lib/who_need_help_web/controllers/content_removal_html.ex:21 #: lib/who_need_help_web/controllers/content_removal_html.ex:21
#: lib/who_need_help_web/controllers/support_html.ex:16 #: lib/who_need_help_web/controllers/support_html.ex:16
#: lib/who_need_help_web/live/activity_live/index.ex:356 #: lib/who_need_help_web/live/activity_live/index.ex:420
#: lib/who_need_help_web/live/activity_live/show.ex:342 #: lib/who_need_help_web/live/activity_live/show.ex:342
#: lib/who_need_help_web/live/moderation_live.ex:258 #: lib/who_need_help_web/live/moderation_live.ex:258
#: lib/who_need_help_web/live/report_live.ex:104 #: lib/who_need_help_web/live/report_live.ex:104
#: lib/who_need_help_web/live/request_live/index.ex:319 #: lib/who_need_help_web/live/request_live/index.ex:405
#: lib/who_need_help_web/live/request_live/index.ex:320 #: lib/who_need_help_web/live/request_live/index.ex:406
#: lib/who_need_help_web/live/request_live/show.ex:951 #: lib/who_need_help_web/live/request_live/show.ex:951
#: lib/who_need_help_web/live/support_operations_live.ex:237 #: lib/who_need_help_web/live/support_operations_live.ex:237
#: lib/who_need_help_web/live/support_operations_live.ex:438 #: lib/who_need_help_web/live/support_operations_live.ex:438
@ -1745,9 +1745,9 @@ msgid "Save profile"
msgstr "Зберегти профіль" msgstr "Зберегти профіль"
#: lib/who_need_help_web/live/notification_live.ex:750 #: lib/who_need_help_web/live/notification_live.ex:750
#: lib/who_need_help_web/live/request_live/index.ex:315 #: lib/who_need_help_web/live/request_live/index.ex:401
#: lib/who_need_help_web/live/request_live/index.ex:316 #: lib/who_need_help_web/live/request_live/index.ex:402
#: lib/who_need_help_web/live/request_live/index.ex:425 #: lib/who_need_help_web/live/request_live/index.ex:511
#: lib/who_need_help_web/live/request_live/new.ex:451 #: lib/who_need_help_web/live/request_live/new.ex:451
#: lib/who_need_help_web/live/request_live/show.ex:947 #: lib/who_need_help_web/live/request_live/show.ex:947
#: lib/who_need_help_web/live/request_live/show.ex:948 #: lib/who_need_help_web/live/request_live/show.ex:948
@ -2019,7 +2019,7 @@ msgstr "Цей стан активності вже змінено."
msgid "This interaction is blocked." msgid "This interaction is blocked."
msgstr "Ця взаємодія заблокована." msgstr "Ця взаємодія заблокована."
#: lib/who_need_help_web/live/request_live/index.ex:363 #: lib/who_need_help_web/live/request_live/index.ex:449
#: lib/who_need_help_web/live/request_live/show.ex:1022 #: lib/who_need_help_web/live/request_live/show.ex:1022
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "This is not an emergency service. Contact local emergency services for immediate danger." msgid "This is not an emergency service. Contact local emergency services for immediate danger."
@ -2072,9 +2072,9 @@ msgid "To see sent emails, visit"
msgstr "Щоб переглянути надіслані листи, відкрийте" msgstr "Щоб переглянути надіслані листи, відкрийте"
#: lib/who_need_help_web/live/notification_live.ex:749 #: lib/who_need_help_web/live/notification_live.ex:749
#: lib/who_need_help_web/live/request_live/index.ex:313 #: lib/who_need_help_web/live/request_live/index.ex:399
#: lib/who_need_help_web/live/request_live/index.ex:314 #: lib/who_need_help_web/live/request_live/index.ex:400
#: lib/who_need_help_web/live/request_live/index.ex:424 #: lib/who_need_help_web/live/request_live/index.ex:510
#: lib/who_need_help_web/live/request_live/new.ex:450 #: lib/who_need_help_web/live/request_live/new.ex:450
#: lib/who_need_help_web/live/request_live/show.ex:945 #: lib/who_need_help_web/live/request_live/show.ex:945
#: lib/who_need_help_web/live/request_live/show.ex:946 #: lib/who_need_help_web/live/request_live/show.ex:946
@ -2149,7 +2149,7 @@ msgid "Update email instructions"
msgstr "Інструкції для зміни електронної пошти" msgstr "Інструкції для зміни електронної пошти"
#: lib/who_need_help_web/live/notification_live.ex:743 #: lib/who_need_help_web/live/notification_live.ex:743
#: lib/who_need_help_web/live/request_live/index.ex:420 #: lib/who_need_help_web/live/request_live/index.ex:506
#: lib/who_need_help_web/live/request_live/new.ex:447 #: lib/who_need_help_web/live/request_live/new.ex:447
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Urgency" msgid "Urgency"
@ -2304,7 +2304,7 @@ msgstr ""
"через підписку ChatGPT розробника. Ключ OpenAI API та резервний варіант API " "через підписку ChatGPT розробника. Ключ OpenAI API та резервний варіант API "
"з оплатою за використання не застосовуються." "з оплатою за використання не застосовуються."
#: lib/who_need_help_web/live/request_live/index.ex:348 #: lib/who_need_help_web/live/request_live/index.ex:434
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Who needs help?" msgid "Who needs help?"
msgstr "Хто потребує допомоги?" msgstr "Хто потребує допомоги?"
@ -2426,7 +2426,7 @@ msgid "Your review is revealed only after both participants submit."
msgstr "" msgstr ""
"Ваш відгук відкриється лише після того, як його надішлють обидва учасники." "Ваш відгук відкриється лише після того, як його надішлють обидва учасники."
#: lib/who_need_help_web/live/request_live/index.ex:630 #: lib/who_need_help_web/live/request_live/index.ex:724
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Your trust summary" msgid "Your trust summary"
msgstr "Ваші показники довіри" msgstr "Ваші показники довіри"
@ -2447,17 +2447,17 @@ msgstr "повідомлення активності"
msgid "before confirming." msgid "before confirming."
msgstr "перед підтвердженням." msgstr "перед підтвердженням."
#: lib/who_need_help_web/live/activity_live/index.ex:577 #: lib/who_need_help_web/live/activity_live/index.ex:646
#: lib/who_need_help_web/live/category_proposal_live.ex:252 #: lib/who_need_help_web/live/category_proposal_live.ex:252
#: lib/who_need_help_web/live/moderation_live.ex:372 #: lib/who_need_help_web/live/moderation_live.ex:372
#: lib/who_need_help_web/live/request_live/index.ex:564 #: lib/who_need_help_web/live/request_live/index.ex:655
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "by %{name}" msgid "by %{name}"
msgstr "автор: %{name}" msgstr "автор: %{name}"
#: lib/who_need_help_web/live/profile_live.ex:190 #: lib/who_need_help_web/live/profile_live.ex:190
#: lib/who_need_help_web/live/public_profile_live.ex:133 #: lib/who_need_help_web/live/public_profile_live.ex:133
#: lib/who_need_help_web/live/request_live/index.ex:634 #: lib/who_need_help_web/live/request_live/index.ex:728
#: lib/who_need_help_web/live/request_live/show.ex:1374 #: lib/who_need_help_web/live/request_live/show.ex:1374
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "completed" msgid "completed"
@ -2493,7 +2493,7 @@ msgstr "організатор"
msgid "participants only" msgid "participants only"
msgstr "лише учасники" msgstr "лише учасники"
#: lib/who_need_help_web/live/request_live/index.ex:638 #: lib/who_need_help_web/live/request_live/index.ex:732
#: lib/who_need_help_web/live/request_live/show.ex:1378 #: lib/who_need_help_web/live/request_live/show.ex:1378
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "people" msgid "people"
@ -2553,7 +2553,7 @@ msgstr "терміново"
#: lib/who_need_help_web/live/activity_live/show.ex:719 #: lib/who_need_help_web/live/activity_live/show.ex:719
#: lib/who_need_help_web/live/profile_live.ex:285 #: lib/who_need_help_web/live/profile_live.ex:285
#: lib/who_need_help_web/live/public_profile_live.ex:98 #: lib/who_need_help_web/live/public_profile_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:642 #: lib/who_need_help_web/live/request_live/index.ex:736
#: lib/who_need_help_web/live/request_live/show.ex:1093 #: lib/who_need_help_web/live/request_live/show.ex:1093
#: lib/who_need_help_web/live/request_live/show.ex:1476 #: lib/who_need_help_web/live/request_live/show.ex:1476
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2586,8 +2586,8 @@ msgstr "← Заявки"
msgid "Approved" msgid "Approved"
msgstr "Схвалено" msgstr "Схвалено"
#: lib/who_need_help_web/live/request_live/index.ex:329 #: lib/who_need_help_web/live/request_live/index.ex:415
#: lib/who_need_help_web/live/request_live/index.ex:330 #: lib/who_need_help_web/live/request_live/index.ex:416
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expired" msgid "Expired"
msgstr "Спливло" msgstr "Спливло"
@ -2640,8 +2640,8 @@ msgstr "Незвична частота дій"
msgid "Load earlier messages" msgid "Load earlier messages"
msgstr "Завантажити попередні повідомлення" msgstr "Завантажити попередні повідомлення"
#: lib/who_need_help_web/live/activity_live/index.ex:589 #: lib/who_need_help_web/live/activity_live/index.ex:658
#: lib/who_need_help_web/live/activity_live/index.ex:679 #: lib/who_need_help_web/live/activity_live/index.ex:751
#: lib/who_need_help_web/live/category_proposal_live.ex:284 #: lib/who_need_help_web/live/category_proposal_live.ex:284
#: lib/who_need_help_web/live/leaderboard_live.ex:91 #: lib/who_need_help_web/live/leaderboard_live.ex:91
#: lib/who_need_help_web/live/moderation_live.ex:465 #: lib/who_need_help_web/live/moderation_live.ex:465
@ -2653,7 +2653,7 @@ msgstr "Завантажити попередні повідомлення"
#: lib/who_need_help_web/live/profile_live.ex:363 #: lib/who_need_help_web/live/profile_live.ex:363
#: lib/who_need_help_web/live/public_profile_live.ex:178 #: lib/who_need_help_web/live/public_profile_live.ex:178
#: lib/who_need_help_web/live/report_live.ex:98 #: lib/who_need_help_web/live/report_live.ex:98
#: lib/who_need_help_web/live/request_live/index.ex:699 #: lib/who_need_help_web/live/request_live/index.ex:805
#: lib/who_need_help_web/live/support_operations_live.ex:306 #: lib/who_need_help_web/live/support_operations_live.ex:306
#: lib/who_need_help_web/live/support_operations_live.ex:505 #: lib/who_need_help_web/live/support_operations_live.ex:505
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -2661,7 +2661,7 @@ msgid "Load more"
msgstr "Завантажити ще" msgstr "Завантажити ще"
#: lib/who_need_help_web/live/activity_live/show.ex:544 #: lib/who_need_help_web/live/activity_live/show.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:610 #: lib/who_need_help_web/live/request_live/index.ex:701
#: lib/who_need_help_web/live/request_live/show.ex:1275 #: lib/who_need_help_web/live/request_live/show.ex:1275
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Request details remain usable." msgid "The map is unavailable in this browser. Request details remain usable."
@ -4650,7 +4650,7 @@ msgstr "Перетягніть, щоб перемістити точну точ
msgid "Exact point selected. Drag the red pin or click the map to move it." msgid "Exact point selected. Drag the red pin or click the map to move it."
msgstr "Точну точку вибрано. Перетягніть червоний маркер або натисніть на карту, щоб перемістити її." msgstr "Точну точку вибрано. Перетягніть червоний маркер або натисніть на карту, щоб перемістити її."
#: lib/who_need_help_web/live/request_live/index.ex:453 #: lib/who_need_help_web/live/request_live/index.ex:539
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} request in this map area" msgid "%{count} request in this map area"
msgid_plural "%{count} requests in this map area" msgid_plural "%{count} requests in this map area"
@ -4658,37 +4658,37 @@ msgstr[0] "%{count} заявка в цій області карти"
msgstr[1] "%{count} заявки в цій області карти" msgstr[1] "%{count} заявки в цій області карти"
msgstr[2] "%{count} заявок у цій області карти" msgstr[2] "%{count} заявок у цій області карти"
#: lib/who_need_help_web/live/activity_live/index.ex:438 #: lib/who_need_help_web/live/activity_live/index.ex:502
#: lib/who_need_help_web/live/request_live/index.ex:430 #: lib/who_need_help_web/live/request_live/index.ex:516
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Area or city" msgid "Area or city"
msgstr "Район або місто" msgstr "Район або місто"
#: lib/who_need_help_web/live/activity_live/index.ex:525 #: lib/who_need_help_web/live/activity_live/index.ex:594
#: lib/who_need_help_web/live/request_live/index.ex:515 #: lib/who_need_help_web/live/request_live/index.ex:606
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Choose an area on the map" msgid "Choose an area on the map"
msgstr "Виберіть область на карті" msgstr "Виберіть область на карті"
#: lib/who_need_help_web/live/request_live/index.ex:350 #: lib/who_need_help_web/live/request_live/index.ex:436
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters." msgid "Explore privacy-friendly areas and public exact meeting points chosen by requesters."
msgstr "Переглядайте приватні області та точні публічні місця зустрічі, вибрані авторами заявок." msgstr "Переглядайте приватні області та точні публічні місця зустрічі, вибрані авторами заявок."
#: lib/who_need_help_web/live/activity_live/index.ex:484 #: lib/who_need_help_web/live/activity_live/index.ex:553
#: lib/who_need_help_web/live/request_live/index.ex:474 #: lib/who_need_help_web/live/request_live/index.ex:565
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "List" msgid "List"
msgstr "Список" msgstr "Список"
#: lib/who_need_help_web/live/request_live/index.ex:586 #: lib/who_need_help_web/live/request_live/index.ex:677
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Load more in this area" msgid "Load more in this area"
msgstr "Завантажити ще в цій області" msgstr "Завантажити ще в цій області"
#: lib/who_need_help_web/components/layouts/root.html.heex:5 #: lib/who_need_help_web/components/layouts/root.html.heex:5
#: lib/who_need_help_web/live/activity_live/index.ex:504 #: lib/who_need_help_web/live/activity_live/index.ex:573
#: lib/who_need_help_web/live/request_live/index.ex:494 #: lib/who_need_help_web/live/request_live/index.ex:585
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Map" msgid "Map"
msgstr "Карта" msgstr "Карта"
@ -4698,53 +4698,53 @@ msgstr "Карта"
msgid "Map preview" msgid "Map preview"
msgstr "Попередній перегляд карти" msgstr "Попередній перегляд карти"
#: lib/who_need_help_web/live/activity_live/index.ex:537 #: lib/who_need_help_web/live/activity_live/index.ex:606
#: lib/who_need_help_web/live/request_live/index.ex:527 #: lib/who_need_help_web/live/request_live/index.ex:618
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move or zoom the map to search somewhere else." msgid "Move or zoom the map to search somewhere else."
msgstr "Перемістіть або масштабуйте карту, щоб шукати в іншому місці." msgstr "Перемістіть або масштабуйте карту, щоб шукати в іншому місці."
#: lib/who_need_help_web/live/activity_live/index.ex:471 #: lib/who_need_help_web/live/activity_live/index.ex:540
#: lib/who_need_help_web/live/request_live/index.ex:461 #: lib/who_need_help_web/live/request_live/index.ex:552
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move the map, then search the newly visible area." msgid "Move the map, then search the newly visible area."
msgstr "Перемістіть карту, а потім виконайте пошук у новій видимій області." msgstr "Перемістіть карту, а потім виконайте пошук у новій видимій області."
#: lib/who_need_help_web/live/request_live/index.ex:525 #: lib/who_need_help_web/live/request_live/index.ex:616
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open requests in this area" msgid "No open requests in this area"
msgstr "У цій області немає відкритих заявок" msgstr "У цій області немає відкритих заявок"
#: lib/who_need_help_web/live/request_live/index.ex:517 #: lib/who_need_help_web/live/request_live/index.ex:608
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only requests from the selected map area will be loaded." msgid "Only requests from the selected map area will be loaded."
msgstr "Буде завантажено лише заявки з вибраної області карти." msgstr "Буде завантажено лише заявки з вибраної області карти."
#: lib/who_need_help_web/components/layouts/root.html.heex:8 #: lib/who_need_help_web/components/layouts/root.html.heex:8
#: lib/who_need_help_web/live/request_live/index.ex:613 #: lib/who_need_help_web/live/request_live/index.ex:704
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open request" msgid "Open request"
msgstr "Відкрити заявку" msgstr "Відкрити заявку"
#: lib/who_need_help_web/live/activity_live/index.ex:439 #: lib/who_need_help_web/live/activity_live/index.ex:503
#: lib/who_need_help_web/live/request_live/index.ex:431 #: lib/who_need_help_web/live/request_live/index.ex:517
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Optional text search" msgid "Optional text search"
msgstr "Необов’язковий текстовий пошук" msgstr "Необов’язковий текстовий пошук"
#: lib/who_need_help_web/live/request_live/index.ex:592 #: lib/who_need_help_web/live/request_live/index.ex:683
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests without map coordinates appear only when their public area description matches the text search." msgid "Requests without map coordinates appear only when their public area description matches the text search."
msgstr "Заявки без координат на карті з’являються лише тоді, коли опис публічної області збігається з текстовим пошуком." msgstr "Заявки без координат на карті з’являються лише тоді, коли опис публічної області збігається з текстовим пошуком."
#: lib/who_need_help_web/live/activity_live/index.ex:475 #: lib/who_need_help_web/live/activity_live/index.ex:544
#: lib/who_need_help_web/live/request_live/index.ex:465 #: lib/who_need_help_web/live/request_live/index.ex:556
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Results layout" msgid "Results layout"
msgstr "Розташування результатів" msgstr "Розташування результатів"
#: lib/who_need_help_web/live/activity_live/index.ex:621 #: lib/who_need_help_web/live/activity_live/index.ex:693
#: lib/who_need_help_web/live/request_live/index.ex:625 #: lib/who_need_help_web/live/request_live/index.ex:719
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search this area" msgid "Search this area"
msgstr "Шукати в цій області" msgstr "Шукати в цій області"
@ -4754,21 +4754,21 @@ msgstr "Шукати в цій області"
msgid "Static preview" msgid "Static preview"
msgstr "Статичний перегляд" msgstr "Статичний перегляд"
#: lib/who_need_help_web/live/activity_live/index.ex:494 #: lib/who_need_help_web/live/activity_live/index.ex:563
#: lib/who_need_help_web/live/request_live/index.ex:484 #: lib/who_need_help_web/live/request_live/index.ex:575
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Together" msgid "Together"
msgstr "Разом" msgstr "Разом"
#: lib/who_need_help_web/components/layouts/root.html.heex:6 #: lib/who_need_help_web/components/layouts/root.html.heex:6
#: lib/who_need_help_web/controllers/page_html/home.html.heex:160 #: lib/who_need_help_web/controllers/page_html/home.html.heex:160
#: lib/who_need_help_web/live/activity_live/index.ex:609 #: lib/who_need_help_web/live/activity_live/index.ex:681
#: lib/who_need_help_web/live/activity_live/new.ex:355 #: lib/who_need_help_web/live/activity_live/new.ex:355
#: lib/who_need_help_web/live/activity_live/new.ex:414 #: lib/who_need_help_web/live/activity_live/new.ex:414
#: lib/who_need_help_web/live/activity_live/show.ex:546 #: lib/who_need_help_web/live/activity_live/show.ex:546
#: lib/who_need_help_web/live/notification_live.ex:602 #: lib/who_need_help_web/live/notification_live.ex:602
#: lib/who_need_help_web/live/notification_live.ex:621 #: lib/who_need_help_web/live/notification_live.ex:621
#: lib/who_need_help_web/live/request_live/index.ex:612 #: lib/who_need_help_web/live/request_live/index.ex:703
#: lib/who_need_help_web/live/request_live/new.ex:485 #: lib/who_need_help_web/live/request_live/new.ex:485
#: lib/who_need_help_web/live/request_live/new.ex:563 #: lib/who_need_help_web/live/request_live/new.ex:563
#: lib/who_need_help_web/live/request_live/show.ex:1277 #: lib/who_need_help_web/live/request_live/show.ex:1277
@ -5158,7 +5158,7 @@ msgid "Nearby alert deleted."
msgstr "Сповіщення поблизу видалено." msgstr "Сповіщення поблизу видалено."
#: lib/who_need_help_web/live/notification_live.ex:456 #: lib/who_need_help_web/live/notification_live.ex:456
#: lib/who_need_help_web/live/request_live/index.ex:376 #: lib/who_need_help_web/live/request_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby requests" msgid "Nearby requests"
msgstr "Заявки поблизу" msgstr "Заявки поблизу"
@ -5498,7 +5498,7 @@ msgid "WebGL is unavailable in this browser, so the full map cannot start. Selec
msgstr "WebGL недоступний у цьому браузері, тому повна карта не може запуститися. Виберіть приклад нижче або спробуйте запустити карту ще раз." msgstr "WebGL недоступний у цьому браузері, тому повна карта не може запуститися. Виберіть приклад нижче або спробуйте запустити карту ще раз."
#: lib/who_need_help_web/components/layouts/root.html.heex:7 #: lib/who_need_help_web/components/layouts/root.html.heex:7
#: lib/who_need_help_web/live/activity_live/index.ex:610 #: lib/who_need_help_web/live/activity_live/index.ex:682
#: lib/who_need_help_web/live/activity_live/show.ex:547 #: lib/who_need_help_web/live/activity_live/show.ex:547
#: lib/who_need_help_web/live/request_live/show.ex:1278 #: lib/who_need_help_web/live/request_live/show.ex:1278
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -6063,7 +6063,7 @@ msgstr "Тепер ваш відгук видно"
msgid "Delivery settings" msgid "Delivery settings"
msgstr "Налаштування доставки" msgstr "Налаштування доставки"
#: lib/who_need_help_web/live/request_live/index.ex:569 #: lib/who_need_help_web/live/request_live/index.ex:660
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Expires" msgid "Expires"
msgstr "Спливає" msgstr "Спливає"
@ -6083,12 +6083,12 @@ msgstr "Сповіщення поруч"
msgid "Notification sections" msgid "Notification sections"
msgstr "Розділи сповіщень" msgstr "Розділи сповіщень"
#: lib/who_need_help_web/live/activity_live/index.ex:605 #: lib/who_need_help_web/live/activity_live/index.ex:674
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activities" msgid "%{count} activities"
msgstr "Активностей: %{count}" msgstr "Активностей: %{count}"
#: lib/who_need_help_web/live/activity_live/index.ex:463 #: lib/who_need_help_web/live/activity_live/index.ex:527
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} activity in this map area" msgid "%{count} activity in this map area"
msgid_plural "%{count} activities in this map area" msgid_plural "%{count} activities in this map area"
@ -6096,17 +6096,17 @@ msgstr[0] "%{count} активність у цій області карти"
msgstr[1] "%{count} активності в цій області карти" msgstr[1] "%{count} активності в цій області карти"
msgstr[2] "%{count} активностей у цій області карти" msgstr[2] "%{count} активностей у цій області карти"
#: lib/who_need_help_web/live/request_live/index.ex:614 #: lib/who_need_help_web/live/request_live/index.ex:705
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{count} requests" msgid "%{count} requests"
msgstr "Запитів: %{count}" msgstr "Запитів: %{count}"
#: lib/who_need_help_web/live/activity_live/index.ex:645 #: lib/who_need_help_web/live/activity_live/index.ex:717
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list." msgid "Activities you create or ask to join will appear here. Leaving removes an activity from this list."
msgstr "Створені вами активності й ті, до яких ви попросили приєднатися, з’являться тут. Після виходу активність зникне зі списку." msgstr "Створені вами активності й ті, до яких ви попросили приєднатися, з’являться тут. Після виходу активність зникне зі списку."
#: lib/who_need_help_web/live/activity_live/index.ex:393 #: lib/who_need_help_web/live/activity_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Activity views" msgid "Activity views"
msgstr "Розділи активностей" msgstr "Розділи активностей"
@ -6121,12 +6121,12 @@ msgstr "Оберіть категорію та зрозуміло опишіть
msgid "Confirm the safety guidance before asking nearby people for help." msgid "Confirm the safety guidance before asking nearby people for help."
msgstr "Підтвердьте правила безпеки перед публікацією запиту." msgstr "Підтвердьте правила безпеки перед публікацією запиту."
#: lib/who_need_help_web/live/request_live/index.ex:659 #: lib/who_need_help_web/live/request_live/index.ex:753
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create request" msgid "Create request"
msgstr "Створити запит" msgstr "Створити запит"
#: lib/who_need_help_web/live/request_live/index.ex:673 #: lib/who_need_help_web/live/request_live/index.ex:776
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Create your first request" msgid "Create your first request"
msgstr "Створити перший запит" msgstr "Створити перший запит"
@ -6141,43 +6141,43 @@ msgstr "Що потрібно зробити"
msgid "Location" msgid "Location"
msgstr "Місце" msgstr "Місце"
#: lib/who_need_help_web/live/activity_live/index.ex:399 #: lib/who_need_help_web/live/activity_live/index.ex:463
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My activities" msgid "My activities"
msgstr "Мої активності" msgstr "Мої активності"
#: lib/who_need_help_web/live/request_live/index.ex:377 #: lib/who_need_help_web/live/request_live/index.ex:463
#: lib/who_need_help_web/live/request_live/index.ex:653 #: lib/who_need_help_web/live/request_live/index.ex:747
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "My requests" msgid "My requests"
msgstr "Мої запити" msgstr "Мої запити"
#: lib/who_need_help_web/live/activity_live/index.ex:398 #: lib/who_need_help_web/live/activity_live/index.ex:462
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Nearby activities" msgid "Nearby activities"
msgstr "Активності поруч" msgstr "Активності поруч"
#: lib/who_need_help_web/live/activity_live/index.ex:643 #: lib/who_need_help_web/live/activity_live/index.ex:715
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No activities yet" msgid "No activities yet"
msgstr "Активностей поки немає" msgstr "Активностей поки немає"
#: lib/who_need_help_web/live/activity_live/index.ex:535 #: lib/who_need_help_web/live/activity_live/index.ex:604
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No open activities in this area" msgid "No open activities in this area"
msgstr "У цій області немає відкритих активностей" msgstr "У цій області немає відкритих активностей"
#: lib/who_need_help_web/live/request_live/index.ex:668 #: lib/who_need_help_web/live/request_live/index.ex:771
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No requests yet" msgid "No requests yet"
msgstr "Запитів поки немає" msgstr "Запитів поки немає"
#: lib/who_need_help_web/live/activity_live/index.ex:527 #: lib/who_need_help_web/live/activity_live/index.ex:596
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Only activities from the selected map area will be loaded." msgid "Only activities from the selected map area will be loaded."
msgstr "Буде завантажено лише активності з вибраної області карти." msgstr "Буде завантажено лише активності з вибраної області карти."
#: lib/who_need_help_web/live/activity_live/index.ex:604 #: lib/who_need_help_web/live/activity_live/index.ex:673
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Open activity" msgid "Open activity"
msgstr "Відкрити активність" msgstr "Відкрити активність"
@ -6187,12 +6187,12 @@ msgstr "Відкрити активність"
msgid "Request form steps" msgid "Request form steps"
msgstr "Кроки створення запиту" msgstr "Кроки створення запиту"
#: lib/who_need_help_web/live/request_live/index.ex:371 #: lib/who_need_help_web/live/request_live/index.ex:457
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Request views" msgid "Request views"
msgstr "Розділи запитів" msgstr "Розділи запитів"
#: lib/who_need_help_web/live/request_live/index.ex:670 #: lib/who_need_help_web/live/request_live/index.ex:773
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Requests you publish or accept will appear here." msgid "Requests you publish or accept will appear here."
msgstr "Тут з’являться запити, які ви створили або прийняли." msgstr "Тут з’являться запити, які ви створили або прийняли."
@ -6217,7 +6217,7 @@ msgstr "Укажіть терміновість і час закриття за
msgid "Share only as much location detail as helpers need." msgid "Share only as much location detail as helpers need."
msgstr "Покажіть помічникам лише необхідну інформацію про місце." msgstr "Покажіть помічникам лише необхідну інформацію про місце."
#: lib/who_need_help_web/live/activity_live/index.ex:607 #: lib/who_need_help_web/live/activity_live/index.ex:679
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "The map is unavailable in this browser. Activity details remain usable." msgid "The map is unavailable in this browser. Activity details remain usable."
msgstr "Карта недоступна в цьому браузері. Інформація про активності залишається доступною." msgstr "Карта недоступна в цьому браузері. Інформація про активності залишається доступною."
@ -6227,7 +6227,7 @@ msgstr "Карта недоступна в цьому браузері. Інфо
msgid "Timing" msgid "Timing"
msgstr "Час" msgstr "Час"
#: lib/who_need_help_web/live/request_live/index.ex:655 #: lib/who_need_help_web/live/request_live/index.ex:749
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Track requests you created and requests you accepted as a helper." msgid "Track requests you created and requests you accepted as a helper."
msgstr "Стежте за створеними запитами й тими, які ви прийняли як помічник." msgstr "Стежте за створеними запитами й тими, які ви прийняли як помічник."
@ -6888,11 +6888,32 @@ msgstr "Що мають бачити люди до схвалення?"
msgid "Choose the meeting point on the map or use your current location." msgid "Choose the meeting point on the map or use your current location."
msgstr "Оберіть місце зустрічі на карті або скористайтеся своїм поточним місцезнаходженням." msgstr "Оберіть місце зустрічі на карті або скористайтеся своїм поточним місцезнаходженням."
#: lib/who_need_help_web/live/activity_live/index.ex:458 #: lib/who_need_help_web/live/activity_live/index.ex:522
#: lib/who_need_help_web/live/activity_live/index.ex:517 #: lib/who_need_help_web/live/activity_live/index.ex:586
#: lib/who_need_help_web/live/activity_live/index.ex:635 #: lib/who_need_help_web/live/activity_live/index.ex:707
#: lib/who_need_help_web/live/request_live/index.ex:448 #: lib/who_need_help_web/live/request_live/index.ex:534
#: lib/who_need_help_web/live/request_live/index.ex:507 #: lib/who_need_help_web/live/request_live/index.ex:598
#: lib/who_need_help_web/live/request_live/index.ex:763
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Loading results…" msgid "Loading results…"
msgstr "Завантажуємо результати…" msgstr "Завантажуємо результати…"
#: lib/who_need_help_web/live/activity_live/index.ex:676
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching activities are listed beside the map."
msgstr "Досягнуто максимального масштабу. Відповідні заходи перелічено поруч із картою."
#: lib/who_need_help_web/live/request_live/index.ex:707
#, elixir-autogen, elixir-format
msgid "Maximum zoom reached. Matching requests are listed beside the map."
msgstr "Досягнуто максимального масштабу. Відповідні заявки перелічено поруч із картою."
#: lib/who_need_help_web/live/activity_live/index.ex:535
#, elixir-autogen, elixir-format
msgid "of %{count} matching activities with map locations"
msgstr "із %{count} відповідних заходів із місцезнаходженням на карті"
#: lib/who_need_help_web/live/request_live/index.ex:547
#, elixir-autogen, elixir-format
msgid "of %{count} matching requests with map locations"
msgstr "із %{count} відповідних заявок із місцезнаходженням на карті"

View File

@ -448,6 +448,11 @@ defmodule WhoNeedHelp.ActivitiesTest do
cluster = Enum.find(items, &match?(%{type: "cluster", count: 3}, &1)) cluster = Enum.find(items, &match?(%{type: "cluster", count: 3}, &1))
assert cluster assert cluster
assert cluster.point_count == 3
assert cluster.hierarchy_level == 11
assert String.starts_with?(cluster.cluster_id, "activity:11:")
assert String.starts_with?(cluster.parent_cluster_id, "activity:10:")
assert cluster.expansion_zoom > 9
assert_in_delta cluster.latitude, 50.45, 0.000_001 assert_in_delta cluster.latitude, 50.45, 0.000_001
assert_in_delta cluster.longitude, 30.52, 0.000_001 assert_in_delta cluster.longitude, 30.52, 0.000_001
assert_in_delta cluster.bounds.west, 30.52, 0.000_001 assert_in_delta cluster.bounds.west, 30.52, 0.000_001
@ -456,9 +461,33 @@ defmodule WhoNeedHelp.ActivitiesTest do
assert_in_delta cluster.bounds.north, 50.45, 0.000_001 assert_in_delta cluster.bounds.north, 50.45, 0.000_001
assert Enum.any?(items, fn assert Enum.any?(items, fn
%{type: "activity", id: id} -> id == sparse.id %{
_item -> false type: "activity",
id: id,
hierarchy_level: 11,
parent_cluster_id: "activity:10:" <> _
} ->
id == sparse.id
_item ->
false
end) end)
world_items =
Activities.map_discovery_items(
context.outsider_scope,
%{},
viewport!(-179.999, -85.0, 179.999, 85.0, 0)
)
represented_count =
Enum.reduce(world_items, 0, fn
%{type: "cluster", count: count}, total -> total + count
_point, total -> total + 1
end)
assert represented_count ==
Activities.count_map_discovery_items(context.outsider_scope, %{})
end end
test "scale fixture activities keep distinct synthetic public map coordinates", context do test "scale fixture activities keep distinct synthetic public map coordinates", context do

View File

@ -0,0 +1,43 @@
defmodule WhoNeedHelp.Help.DiscoveryClusterTest do
use ExUnit.Case, async: true
alias WhoNeedHelp.Help.DiscoveryCluster
test "map zooms select stable hierarchy levels" do
assert DiscoveryCluster.level_for_zoom(0) == 2
assert DiscoveryCluster.level_for_zoom(5.49) == 7
assert DiscoveryCluster.level_for_zoom(5.99) == 7
assert DiscoveryCluster.level_for_zoom(6) == 8
assert DiscoveryCluster.level_for_zoom(22) == 24
assert_in_delta(
DiscoveryCluster.cell_size_meters(8),
DiscoveryCluster.cell_size_meters(7) / 2,
0.000_001
)
end
test "cluster ids and parents form a deterministic quadtree" do
assert DiscoveryCluster.cluster_id(:request, 8, 123, 456) == "request:8:123:456"
assert DiscoveryCluster.parent_id(:request, 8, 123, 456) == "request:7:61:228"
assert DiscoveryCluster.parent_id(:activity, 0, 0, 0) == nil
end
test "expansion zoom is the first child level that separates member bounds" do
level = 7
cell = DiscoveryCluster.cell_size_meters(level)
west = -DiscoveryCluster.world_half_meters() + cell * 10 + cell * 0.1
south = -DiscoveryCluster.world_half_meters() + cell * 20 + cell * 0.1
assert DiscoveryCluster.expansion_zoom(
level,
west,
south,
west + cell * 0.6,
south + cell * 0.6
) == 6
assert DiscoveryCluster.expansion_zoom(level, west, south, west, south) == 22
assert DiscoveryCluster.expansion_zoom(24, west, south, west, south) == 22
end
end

View File

@ -109,47 +109,6 @@ defmodule WhoNeedHelp.Help.DiscoveryViewportTest do
end) end)
end end
test "derives cluster detail from both geographic bounds and rendered pixels" do
{:ok, city} =
DiscoveryViewport.cast(%{
west: 30,
south: 50,
east: 31,
north: 51,
zoom: 9,
width: 800,
height: 600
})
{:ok, neighborhood} =
DiscoveryViewport.cast(%{
west: 30.4,
south: 50.4,
east: 30.6,
north: 50.6,
zoom: 13,
width: 800,
height: 600
})
{:ok, wide_canvas} =
DiscoveryViewport.cast(%{
west: 30,
south: 50,
east: 31,
north: 51,
zoom: 9,
width: 1600,
height: 1200
})
assert DiscoveryViewport.cluster_cell_meters(city) >
DiscoveryViewport.cluster_cell_meters(neighborhood)
assert DiscoveryViewport.cluster_cell_meters(city) >
DiscoveryViewport.cluster_cell_meters(wide_canvas)
end
test "serializes stable URL parameters and compares visible areas" do test "serializes stable URL parameters and compares visible areas" do
{:ok, viewport} = {:ok, viewport} =
DiscoveryViewport.cast(%{ DiscoveryViewport.cast(%{

View File

@ -564,6 +564,11 @@ defmodule WhoNeedHelp.MutualAidFlowTest do
cluster = Enum.find(items, &match?(%{type: "cluster", count: 3}, &1)) cluster = Enum.find(items, &match?(%{type: "cluster", count: 3}, &1))
assert cluster assert cluster
assert cluster.point_count == 3
assert cluster.hierarchy_level == 11
assert String.starts_with?(cluster.cluster_id, "request:11:")
assert String.starts_with?(cluster.parent_cluster_id, "request:10:")
assert cluster.expansion_zoom > viewport.zoom
assert_in_delta cluster.latitude, 50.4501, 0.000_001 assert_in_delta cluster.latitude, 50.4501, 0.000_001
assert_in_delta cluster.longitude, 30.52345, 0.000_001 assert_in_delta cluster.longitude, 30.52345, 0.000_001
assert_in_delta cluster.bounds.west, 30.5234, 0.000_001 assert_in_delta cluster.bounds.west, 30.5234, 0.000_001
@ -572,8 +577,17 @@ defmodule WhoNeedHelp.MutualAidFlowTest do
assert_in_delta cluster.bounds.north, 50.4501, 0.000_001 assert_in_delta cluster.bounds.north, 50.4501, 0.000_001
assert Enum.any?(items, fn assert Enum.any?(items, fn
%{type: "request", id: id, exact: true} -> id == sparse_request.id %{
_item -> false type: "request",
id: id,
exact: true,
hierarchy_level: 11,
parent_cluster_id: "request:10:" <> _
} ->
id == sparse_request.id
_item ->
false
end) end)
dense_ids = MapSet.new(dense_requests, & &1.id) dense_ids = MapSet.new(dense_requests, & &1.id)
@ -582,6 +596,21 @@ defmodule WhoNeedHelp.MutualAidFlowTest do
%{type: "request", id: id} -> MapSet.member?(dense_ids, id) %{type: "request", id: id} -> MapSet.member?(dense_ids, id)
_item -> false _item -> false
end) end)
world_items =
Help.map_discovery_items(
context.helper_scope,
%{},
viewport!(-179.999, -85.0, 179.999, 85.0, 0)
)
represented_count =
Enum.reduce(world_items, 0, fn
%{type: "cluster", count: count}, total -> total + count
_point, total -> total + 1
end)
assert represented_count == Help.count_map_discovery_items(context.helper_scope, %{})
end end
test "map discovery does not expose an exact-for-match coordinate", context do test "map discovery does not expose an exact-for-match coordinate", context do