Skip to content

Commit 13b663b

Browse files
authored
Merge pull request #2378 from Freika/dev
1.3.4
2 parents fd0d0bc + 51f55b2 commit 13b663b

File tree

113 files changed

+4881
-536
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+4881
-536
lines changed

.app_version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.3.3
1+
1.3.4

CHANGELOG.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,38 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7-
## [1.3.3] - Unreleased
7+
## [1.3.4] - 2026-03-15
8+
9+
### Changed
10+
11+
- Redesigned onboarding modal with two paths: "I have data" (inline file import) and "Start tracking" (app download + QR code). New users with existing location data can now start importing within 2 clicks of signing up.
12+
- Onboarding completion is now persisted server-side (`settings.onboarding_completed`) instead of relying solely on localStorage, preventing the modal from reappearing after browser data clears.
13+
- Route opacity data migration now runs as a background job instead of inline during migration, improving deployment reliability for large instances.
14+
15+
### Fixed
16+
17+
- Fix admin and supporter tooltip overflowing the page on narrow screens. #1449
18+
- Fix date navigation arrow tooltips overlapping with the navbar on map pages. #2229 #2100
19+
- Fix infinite loading spinner when a trip has no points in its date range. #2293
20+
- Fix Insights monthly digest panels disappearing when switching months. #2305
21+
- Fix suggested visit confirm/decline not removing the visit from the list. #2307
22+
- Fix Stats page reloading when clicking "countries, cities" link. #2270
23+
- Fix map base layer selection not being restored after page reload (Maps v1). #2093
24+
- Fix duplicate country names in stats caused by geocoder returning different spellings. #2044
25+
- Fix total distance display overlapping layer picker when distance is in miles. #2017
26+
- Fix default route opacity displaying as 6000% for new users. #1891
27+
- Fix shared month stats map missing hexagons from the last day of the month. #1934
28+
- Fix Nominatim reverse geocoder producing all places named "Suggested place" instead of actual place names. #2182
29+
- Fix IDL-crossing route segmenter returning inconsistent coordinate types. `unwrapCoordinates` now always returns a uniform array-of-arrays structure. #2038
30+
- Fix a migration taking too long. #2375
31+
- Fix family sharing not including the requesting user's own location. #2153
32+
- The "Destroy" button on the trip page is now orange. #2348
33+
34+
## [1.3.3] - 2026-03-12
35+
36+
### Added
37+
38+
- Better user management with pagination, search, and filtering in the admin panel. Admins can now easily find and manage users based on email, registration date, and activity status.
839

940
### Fixed
1041

Gemfile.lock

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ GEM
146146
data_migrate (11.3.1)
147147
activerecord (>= 6.1)
148148
railties (>= 6.1)
149-
database_consistency (2.0.6)
149+
database_consistency (3.0.0)
150150
activerecord (>= 3.2)
151151
date (3.5.1)
152152
debug (1.11.0)
@@ -177,14 +177,14 @@ GEM
177177
factory_bot (~> 6.5)
178178
railties (>= 6.1.0)
179179
fakeredis (0.1.4)
180-
faraday (2.14.0)
180+
faraday (2.14.1)
181181
faraday-net_http (>= 2.0, < 3.5)
182182
json
183183
logger
184184
faraday-follow_redirects (0.4.0)
185185
faraday (>= 1, < 3)
186-
faraday-net_http (3.4.1)
187-
net-http (>= 0.5.0)
186+
faraday-net_http (3.4.2)
187+
net-http (~> 0.5)
188188
ffaker (2.25.0)
189189
ffi (1.17.2-aarch64-linux-gnu)
190190
ffi (1.17.2-arm-linux-gnu)
@@ -228,7 +228,7 @@ GEM
228228
rdoc (>= 4.0.0)
229229
reline (>= 0.4.2)
230230
jmespath (1.6.2)
231-
json (2.18.0)
231+
json (2.19.1)
232232
json-jwt (1.17.0)
233233
activesupport (>= 4.2)
234234
aes_key_wrap
@@ -274,14 +274,15 @@ GEM
274274
method_source (1.1.0)
275275
mini_mime (1.1.5)
276276
mini_portile2 (2.8.9)
277-
minitest (6.0.1)
277+
minitest (6.0.2)
278+
drb (~> 2.0)
278279
prism (~> 1.5)
279280
msgpack (1.8.0)
280281
multi_json (1.15.0)
281-
multi_xml (0.8.0)
282+
multi_xml (0.8.1)
282283
bigdecimal (>= 3.1, < 5)
283-
net-http (0.6.0)
284-
uri
284+
net-http (0.9.1)
285+
uri (>= 0.11.1)
285286
net-imap (0.5.12)
286287
date
287288
net-protocol
@@ -305,7 +306,7 @@ GEM
305306
racc (~> 1.4)
306307
nokogiri (1.19.0-x86_64-linux-gnu)
307308
racc (~> 1.4)
308-
oauth2 (2.0.17)
309+
oauth2 (2.0.18)
309310
faraday (>= 0.17.3, < 4.0)
310311
jwt (>= 1.0, < 4.0)
311312
logger (~> 1.2)
@@ -324,13 +325,13 @@ GEM
324325
omniauth-github (2.0.1)
325326
omniauth (~> 2.0)
326327
omniauth-oauth2 (~> 1.8)
327-
omniauth-google-oauth2 (1.2.1)
328+
omniauth-google-oauth2 (1.2.2)
328329
jwt (>= 2.9.2)
329330
oauth2 (~> 2.0)
330331
omniauth (~> 2.0)
331332
omniauth-oauth2 (~> 1.8)
332-
omniauth-oauth2 (1.8.0)
333-
oauth2 (>= 1.4, < 3)
333+
omniauth-oauth2 (1.9.0)
334+
oauth2 (>= 2.0.2, < 3)
334335
omniauth (~> 2.0)
335336
omniauth-rails_csrf_protection (2.0.1)
336337
actionpack (>= 4.2)
@@ -392,7 +393,7 @@ GEM
392393
activesupport (>= 3.0.0)
393394
raabro (1.4.0)
394395
racc (1.8.1)
395-
rack (3.2.4)
396+
rack (3.2.5)
396397
rack-attack (6.8.0)
397398
rack (>= 1.0, < 4)
398399
rack-oauth2 (2.3.0)
@@ -608,7 +609,7 @@ GEM
608609
tailwindcss-ruby (3.4.17-x86_64-darwin)
609610
tailwindcss-ruby (3.4.17-x86_64-linux)
610611
thor (1.5.0)
611-
timeout (0.4.4)
612+
timeout (0.6.0)
612613
tsort (0.2.0)
613614
turbo-rails (2.0.20)
614615
actionpack (>= 7.1.0)

app/assets/builds/tailwind.css

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 55 additions & 0 deletions
Loading

app/assets/stylesheets/leaflet_theme.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@
329329
border-radius: 50%;
330330
}
331331

332+
/* Prevent stats control from overlapping layer picker */
333+
.leaflet-control-stats {
334+
max-width: 220px;
335+
overflow: hidden;
336+
text-overflow: ellipsis;
337+
white-space: nowrap;
338+
}
339+
332340
/* Fix bottom controls being cut off */
333341
.leaflet-bottom {
334342
padding-bottom: 10px !important;
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

app/controllers/api/v1/families/locations_controller.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,29 @@ def index
1414
}
1515
end
1616

17+
def history
18+
start_at = params[:start_at]
19+
end_at = params[:end_at]
20+
21+
if start_at.blank? || end_at.blank?
22+
return render json: { error: 'start_at and end_at are required' }, status: :bad_request
23+
end
24+
25+
parsed_start = Time.zone.parse(start_at)
26+
parsed_end = Time.zone.parse(end_at)
27+
28+
return render json: { error: 'Invalid date format' }, status: :bad_request if parsed_start.nil? || parsed_end.nil?
29+
30+
members = Families::Locations.new(current_api_user).history(
31+
start_at: parsed_start,
32+
end_at: parsed_end
33+
)
34+
35+
render json: { members: members }
36+
rescue ArgumentError
37+
render json: { error: 'Invalid date format' }, status: :bad_request
38+
end
39+
1740
private
1841

1942
def ensure_user_in_family!
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
class Api::V1::ImportsController < ApiController
4+
ALLOWED_EXTENSIONS = %w[.gpx .geojson .json .kml .kmz .rec .csv].freeze
5+
6+
before_action :authenticate_active_api_user!, only: %i[create]
7+
before_action :require_write_api!, only: %i[create]
8+
before_action :validate_points_limit, only: %i[create]
9+
before_action :validate_file_type, only: %i[create]
10+
11+
def index
12+
imports = current_api_user
13+
.imports
14+
.select(:id, :name, :source, :status, :created_at, :processed, :points_count, :error_message)
15+
.order(created_at: :desc)
16+
.page(params[:page])
17+
.per([params.fetch(:per_page, 25).to_i, 100].min)
18+
19+
response.set_header('X-Current-Page', imports.current_page.to_s)
20+
response.set_header('X-Total-Pages', imports.total_pages.to_s)
21+
22+
render json: imports.map { |i| serialize_import(i) }
23+
end
24+
25+
def show
26+
import = current_api_user.imports.find(params[:id])
27+
28+
render json: serialize_import(import)
29+
end
30+
31+
def create
32+
unless params[:file].is_a?(ActionDispatch::Http::UploadedFile)
33+
render json: { error: 'Missing required parameter: file' }, status: :unprocessable_entity and return
34+
end
35+
36+
uploaded_file = params[:file]
37+
import_name = generate_unique_import_name(uploaded_file.original_filename)
38+
39+
import = current_api_user.imports.build(name: import_name)
40+
import.file.attach(
41+
io: uploaded_file.tempfile,
42+
filename: uploaded_file.original_filename,
43+
content_type: uploaded_file.content_type || 'application/octet-stream'
44+
)
45+
46+
if import.save
47+
render json: serialize_import(import), status: :created
48+
else
49+
render json: { error: import.errors.full_messages.join(', ') }, status: :unprocessable_entity
50+
end
51+
rescue ActiveRecord::RecordInvalid => e
52+
render json: { error: e.record.errors.full_messages.join(', ') }, status: :unprocessable_entity
53+
rescue StandardError => e
54+
Rails.logger.error "API Import error: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
55+
ExceptionReporter.call(e)
56+
render json: { error: 'An error occurred while processing the import' }, status: :internal_server_error
57+
end
58+
59+
private
60+
61+
def generate_unique_import_name(original_name)
62+
return original_name unless current_api_user.imports.exists?(name: original_name)
63+
64+
basename = File.basename(original_name, File.extname(original_name))
65+
extension = File.extname(original_name)
66+
timestamp = Time.current.strftime('%Y%m%d_%H%M%S')
67+
"#{basename}_#{timestamp}#{extension}"
68+
end
69+
70+
def validate_file_type
71+
return unless params[:file].is_a?(ActionDispatch::Http::UploadedFile)
72+
73+
ext = File.extname(params[:file].original_filename).downcase
74+
return if ALLOWED_EXTENSIONS.include?(ext)
75+
76+
render json: {
77+
error: "Unsupported file type '#{ext}'. Allowed: #{ALLOWED_EXTENSIONS.join(', ')}"
78+
}, status: :unprocessable_entity
79+
end
80+
81+
def serialize_import(import)
82+
{
83+
id: import.id,
84+
name: import.name,
85+
source: import.source,
86+
status: import.status,
87+
created_at: import.created_at,
88+
points_count: import.points_count,
89+
processed: import.processed,
90+
error_message: import.error_message
91+
}
92+
end
93+
end

0 commit comments

Comments
 (0)