Dates: serve the display wall clock (wall_clock) to clients
Every payload a client renders a capture time from now carries `wall_clock`, a naive ISO-8601 string computed by the existing `date_view::row_display_wall`: the camera's wall clock (true instant + camera offset; the owner's display zone without an offset), with the per-source fallback for unconverted rows. Clients render it verbatim and do no zone math, which is the FileViewer-React #33 / Apollo #37 flip with the rule kept server-side: the drain's permanent `date_utc IS NULL` floor (#120: edited/exported files) would otherwise force every client to carry the per-source legacy conventions forever, and insight prompts already use the same function, so prompt and UI agree by construction. - `/image/metadata` (+ the GPS / date / date-clear mutation responses): `file_wall_clock` — the row's wall, else the filename-class date, else the earliest fs time in the display zone (unindexed files). - `/memories` items and `/photos/gps-summary` photos: `row_wall_clock`. - `get_all_with_gps` returns full rows instead of a 7-tuple that dropped `date_taken_source`, which unconverted rows need. - `date_view::owner_display_zone` resolves prefs -> app default per request. Legacy fields are untouched, so current clients see byte-identical responses (additive; safe to deploy ahead of the client flip, per #120). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e0c6c696f2
commit
e2f33684c8
8 files changed
+257
-93
No files matched your search
+10
-57
@@ -389,25 +389,15 @@ pub trait ExifDao: Sync + Send {
|
||||
/// Get all file paths from EXIF database
|
||||
fn get_all_file_paths(&mut self) -> Result<Vec<String>, DbError>;
|
||||
|
||||
/// Get all photos with GPS coordinates
|
||||
/// Returns Vec<(file_path, latitude, longitude, date_taken)>
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Get all rows with GPS coordinates (both latitude and longitude set)
|
||||
/// under `base_path`. Full rows, so callers can apply the date contract
|
||||
/// (`date_view`) — which needs the date source and path, not just the
|
||||
/// new-contract triple.
|
||||
fn get_all_with_gps(
|
||||
&mut self,
|
||||
base_path: &str,
|
||||
recursive: bool,
|
||||
) -> Result<
|
||||
Vec<(
|
||||
String,
|
||||
f64,
|
||||
f64,
|
||||
Option<i64>,
|
||||
Option<i64>,
|
||||
Option<i32>,
|
||||
Option<String>,
|
||||
)>,
|
||||
DbError,
|
||||
>;
|
||||
) -> Result<Vec<ImageExif>, DbError>;
|
||||
|
||||
/// Return rows that still lack a `content_hash`, oldest first. Used by
|
||||
/// the `backfill_hashes` binary to batch through the historical
|
||||
@@ -1168,23 +1158,11 @@ impl ExifDao for SqliteExifDao {
|
||||
.map_err(|e| DbError::log(DbErrorKind::QueryError, e))
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn get_all_with_gps(
|
||||
&mut self,
|
||||
base_path: &str,
|
||||
recursive: bool,
|
||||
) -> Result<
|
||||
Vec<(
|
||||
String,
|
||||
f64,
|
||||
f64,
|
||||
Option<i64>,
|
||||
Option<i64>,
|
||||
Option<i32>,
|
||||
Option<String>,
|
||||
)>,
|
||||
DbError,
|
||||
> {
|
||||
) -> Result<Vec<ImageExif>, DbError> {
|
||||
trace_db_call("query", "get_all_with_gps", |span| {
|
||||
use schema::image_exif::dsl::*;
|
||||
|
||||
@@ -1216,36 +1194,11 @@ impl ExifDao for SqliteExifDao {
|
||||
.load::<ImageExif>(connection)
|
||||
.map_err(|e| anyhow::anyhow!("GPS query error: {}", e))?;
|
||||
|
||||
// Convert to tuple format (path, lat, lon, legacy date_taken,
|
||||
// date_utc, camera_offset_minutes, date_taken_tz_source).
|
||||
// Filter out any rows where GPS is still None (shouldn't happen due to filter)
|
||||
// Cast f32 GPS values to f64 for API compatibility
|
||||
let filtered: Vec<(
|
||||
String,
|
||||
f64,
|
||||
f64,
|
||||
Option<i64>,
|
||||
Option<i64>,
|
||||
Option<i32>,
|
||||
Option<String>,
|
||||
)> = results
|
||||
// The filter already requires both coordinates; keep the guard so
|
||||
// callers can rely on lat/lon being present.
|
||||
let filtered: Vec<ImageExif> = results
|
||||
.into_iter()
|
||||
.filter_map(|exif| {
|
||||
if let (Some(lat_val), Some(lon_val)) = (exif.gps_latitude, exif.gps_longitude)
|
||||
{
|
||||
Some((
|
||||
exif.file_path,
|
||||
lat_val as f64,
|
||||
lon_val as f64,
|
||||
exif.date_taken,
|
||||
exif.date_utc,
|
||||
exif.camera_offset_minutes,
|
||||
exif.date_taken_tz_source,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|exif| exif.gps_latitude.is_some() && exif.gps_longitude.is_some())
|
||||
.collect();
|
||||
|
||||
span.set_attribute("result_count", filtered.len() as i64);
|
||||
|
||||
Reference in new issue
Block a user