docs(core,fuse,gui,cli): US spelling and correct schema lines

User-visible strings, doc comments and docs now say "Favorite" and
"color", matching the `pdfs favorite` command; UI_UX_PLAN.md §8 records
the choice. The changelog's 1.11.x and Unreleased entries said schema 30,
but V31 (`photos.resolved_at`) shipped in 1.11.0. Adds the missing
release links for 1.10.0 to 1.11.1.
This commit is contained in:
nvrl committed 2026-09-23 03:01:27 +02:00
1 parent 8351382ef2
commit 91a5f21340
18 files changed
+78 -74

No files matched your search

+3 -3
View File
@@ -146,11 +146,11 @@ enum Command {
/// Skip this many photos from the start of the timeline.
#[arg(long, default_value_t = 0)]
offset: usize,
/// List only favourited photos.
/// List only favorited photos.
#[arg(long)]
favorites: bool,
},
/// Add or remove a photo's favourite mark.
/// Add or remove a photo's favorite mark.
Favorite {
/// Photo node uid in `volume~link` form (from `pdfs photos`).
uid: String,
@@ -188,7 +188,7 @@ enum Command {
/// and rewrites them at the right time.
///
/// Proton cannot edit a sealed capture time, so a rewrite means re-uploading
/// the photo and trashing the original — favourites and albums are carried
/// the photo and trashing the original — favorites and albums are carried
/// across, and the original goes to Proton's trash, not away. Run it with
/// `--dry-run` first.
RedatePhotos {
+5 -5
View File
@@ -63,7 +63,7 @@ pub enum Request {
/// timeline. Like `kind`, the offset is relative to the filtered set.
#[serde(default)]
range: Option<(i64, i64)>,
/// Restrict the page to favourited photos. Older front-ends omit it and
/// Restrict the page to favorited photos. Older front-ends omit it and
/// get the whole timeline, as before.
#[serde(default)]
favorites: bool,
@@ -181,7 +181,7 @@ pub enum Request {
/// time by more than a day: `IMG-20230219-WA0001.jpg` filed under 2026 is
/// the signature of an import whose metadata sidecars did not match.
///
/// Favourites and album memberships are carried across; the original is
/// Favorites and album memberships are carried across; the original is
/// **trashed**, not deleted, so a bad run is recoverable from Proton's
/// trash. With `dry_run` nothing is uploaded or trashed and the report says
/// what would have been done.
@@ -1395,8 +1395,8 @@ pub struct PhotoItem {
/// the split omit it; a front-end then treats everything as a still photo.
#[serde(default = "default_photo_kind")]
pub kind: PhotoKind,
/// Whether the photo is favourited. Older daemons omit it, which a front-end
/// reads as "not favourited" — the same thing it would show for a photo whose
/// Whether the photo is favorited. Older daemons omit it, which a front-end
/// reads as "not favorited" — the same thing it would show for a photo whose
/// tags it has not learned yet.
#[serde(default)]
pub favorite: bool,
@@ -2703,7 +2703,7 @@ mod tests {
}
}
/// A front-end that predates the favourites filter sends no `favorites`
/// A front-end that predates the favorites filter sends no `favorites`
/// field, and must keep getting the whole timeline rather than an empty page.
#[test]
fn timeline_requests_without_the_favorites_filter_still_decode() {
+2 -2
View File
@@ -178,7 +178,7 @@ impl Db {
ratio: r.get(3)?,
thumb_state: r.get(4)?,
kind: crate::control::PhotoKind::from_i64(r.get(5)?),
// Album rows have no favourite state of their own: an album
// Album rows have no favorite state of their own: an album
// photo on someone else's volume is not in our timeline, and
// favouriting one is not supported yet anyway.
favorite: false,
@@ -239,7 +239,7 @@ impl Db {
ratio: r.get(3)?,
thumb_state: r.get(4)?,
kind: crate::control::PhotoKind::from_i64(r.get(5)?),
// Album rows have no favourite state of their own: an album
// Album rows have no favorite state of their own: an album
// photo on someone else's volume is not in our timeline, and
// favouriting one is not supported yet anyway.
favorite: false,
+2 -2
View File
@@ -684,7 +684,7 @@ CREATE INDEX idx_album_photos_seq ON album_photos(album_uid, seq);
CREATE INDEX idx_album_photos_uid ON album_photos(uid);
";
/// V20: whether a photo is a favourite.
/// V20: whether a photo is a favorite.
///
/// A column rather than a tag table: `Favorite` is the only Proton photo tag a
/// user sets by hand (the rest — Video, Screenshot, Selfie, Raw… — are the
@@ -938,7 +938,7 @@ CREATE INDEX IF NOT EXISTS idx_photos_group ON photos(group_key);
/// Schema v31: which photos a refresh still has to read from the server.
///
/// A refresh used to resolve every photo's node to learn its name, media type,
/// favourite tag, content hash and photo relation — minutes of round-trips for a
/// favorite tag, content hash and photo relation — minutes of round-trips for a
/// library of any size, repeated every time the timeline went stale. `resolved_at`
/// records that the work was already done, so a refresh only reads the photos it
/// has never read and the ones a remote event marked stale (`resolved_at = NULL`).
+2 -2
View File
@@ -455,7 +455,7 @@ impl Db {
/// restricts the page to one tab (Photos / Videos / Raw); `range`, when set,
/// restricts it to a `[from, to)` capture-time window (epoch seconds) — the
/// date scrubber's jump. `offset` is relative to whatever the filters leave.
/// `favorites` restricts the page to favourited photos.
/// `favorites` restricts the page to favorited photos.
///
/// The page holds one row per *group*: the RAW and the JPEG of one shot are
/// one tile. The Raw tab is the exception and lists every raw file, because
@@ -620,7 +620,7 @@ impl Db {
Ok(photos)
}
/// Record a photo's favourite flag locally, after the server accepted the
/// Record a photo's favorite flag locally, after the server accepted the
/// change. A uid the timeline does not hold is a no-op — an album photo on
/// someone else's volume is never in our own `photos` table.
pub fn photos_set_favorite(&self, uid: &str, favorite: bool) -> Result<()> {
+1 -1
View File
@@ -222,7 +222,7 @@ fn favorites_are_remembered_across_refreshes_and_filter_a_page() {
assert_eq!(
favorites.iter().map(|p| p.uid.as_str()).collect::<Vec<_>>(),
["p1"],
"only the favourited photo is in a favourites page"
"only the favorited photo is in a favorites page"
);
// A local toggle survives a refresh that could not resolve the photo's node
+1 -1
View File
@@ -812,7 +812,7 @@ mod tests {
}
#[test]
fn sidecar_supplies_capture_time_and_favourite() {
fn sidecar_supplies_capture_time_and_favorite() {
let mut sidecars = HashMap::new();
sidecar(
&mut sidecars,
+4 -4
View File
@@ -511,7 +511,7 @@ impl PhotoEventPlan {
///
/// `is_echo` claims an event as this daemon's own write. The feed replays our
/// writes back at us and a `NodeUpdated` carries no revision id, so without this
/// every favourite the user toggles here would mark its own photo stale and buy
/// every favorite the user toggles here would mark its own photo stale and buy
/// a resolve of a node we just wrote.
fn classify_photo_events(
events: &[DriveEvent],
@@ -529,7 +529,7 @@ fn classify_photo_events(
if *is_trashed {
plan.gone.push(node_uid.to_string());
} else if !is_echo(node_uid) {
// An untrash, a rename, a favourite toggled on the phone: the
// An untrash, a rename, a favorite toggled on the phone: the
// event says which node changed but never what about it, so
// the uid is marked stale and the next refresh reads it.
plan.touched.push(node_uid.to_string());
@@ -974,7 +974,7 @@ mod tests {
},
DriveEvent::NodeUpdated {
id: DriveEventId::from("e3"),
node_uid: uid("favourited"),
node_uid: uid("favorited"),
parent_node_uid: None,
is_trashed: false,
is_shared: false,
@@ -986,7 +986,7 @@ mod tests {
plan,
PhotoEventPlan {
gone: vec![uid("deleted").to_string(), uid("trashed").to_string()],
touched: vec![uid("favourited").to_string()],
touched: vec![uid("favorited").to_string()],
full: false,
}
);
+2 -2
View File
@@ -387,9 +387,9 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
Some(node_uid) => match core.set_photo_favorite(&node_uid, favorite) {
Ok(()) => CtlResponse::Ok {
message: if favorite {
"added to favourites".to_string()
"added to favorites".to_string()
} else {
"removed from favourites".to_string()
"removed from favorites".to_string()
},
},
Err(e) => CtlResponse::error(e),
+3 -3
View File
@@ -501,7 +501,7 @@ impl Core {
.map_err(|e| CoreError::from_api(&e, "update photo tags"))?;
// The photo event feed replays this write back at us as a bare
// `NodeUpdated`, which is indistinguishable from the same photo being
// favourited on a phone. Claim the echo, or the gallery pays for a
// favorited on a phone. Claim the echo, or the gallery pays for a
// resolve of a node whose one changed field is already stored below.
self.note_self_change(uid);
self.db
@@ -1469,7 +1469,7 @@ impl Core {
NodeKind::File { media_type, .. } => Some(media_type.clone()),
NodeKind::Folder => None,
};
// The favourite state is a tag on the photo node, so it
// The favorite state is a tag on the photo node, so it
// rides along with the metadata resolve rather than
// costing a listing of its own.
let favorite = node
@@ -1507,7 +1507,7 @@ impl Core {
let key = it.uid.to_string();
let main_uid = main_of.get(&key).cloned();
// An unresolved photo keeps whatever was learned before, for the
// favourite flag as much as for the name and media type.
// favorite flag as much as for the name and media type.
match meta.get(&key).cloned() {
Some(m) => db::TimelineRow {
name: m.name,
+3 -3
View File
@@ -8,7 +8,7 @@
//!
//! Proton has no API for editing a sealed capture time, so the repair is a
//! rewrite: download the photo, upload it again with the capture time its file
//! name implies, put the new copy back into the albums and favourites the old
//! name implies, put the new copy back into the albums and favorites the old
//! one was in, then **trash** (not delete) the original. Trash rather than
//! delete so a bad run is recoverable from Proton's own trash for 30 days.
//!
@@ -213,7 +213,7 @@ impl Core {
drop(guard);
let new_uid = uploaded.map_err(|e| CoreError::from_api(&e, "re-upload photo"))?;
// Favourite and albums before the trash: a failure here leaves both
// Favorite and albums before the trash: a failure here leaves both
// copies on the account, which is recoverable by hand. Losing the
// original first would not be.
if candidate.favorite {
@@ -227,7 +227,7 @@ impl Core {
// just set does not come back as a remote change.
Ok(_) => self.note_self_change(&new_uid),
Err(e) => {
warn!(name = candidate.name, error = %e, "cannot re-favourite re-dated photo")
warn!(name = candidate.name, error = %e, "cannot re-favorite re-dated photo")
}
}
}
+3 -3
View File
@@ -357,9 +357,9 @@ impl Core {
}
}
// Favourites are a separate call: the upload seal takes classification
// Favorites are a separate call: the upload seal takes classification
// tags, but `Favorite` is not one of them — it goes through the photos
// volume's dedicated favourite endpoint.
// volume's dedicated favorite endpoint.
if !favorites.is_empty() {
let updates: Vec<PhotoTagsUpdate> = favorites
.into_iter()
@@ -370,7 +370,7 @@ impl Core {
})
.collect();
if let Err(e) = self.rt.block_on(photos.update_photos(&updates)) {
warn!(error = %e, "marking imported favourites failed");
warn!(error = %e, "marking imported favorites failed");
}
}
Ok(())
@@ -19,7 +19,7 @@ pub(crate) struct Viewer {
pub(crate) info_map: gtk4::Button,
/// Coordinates behind `info_map`, once a photo with GPS tags is shown.
pub(crate) coords: RefCell<Option<(f64, f64)>>,
/// The favourite toggle in the top bar, and whether it is being set from
/// The favorite toggle in the top bar, and whether it is being set from
/// the model rather than by the user — the same suppression the details pane
/// uses for its pin switch, so painting a photo doesn't fire a round-trip.
pub(crate) favorite: gtk4::ToggleButton,
@@ -51,7 +51,7 @@ pub(crate) struct ExifInfo {
pub(crate) coords: Option<(f64, f64)>,
}
/// Paint the favourite toggle without firing its handler.
/// Paint the favorite toggle without firing its handler.
pub(crate) fn show_favorite(viewer: &Rc<Viewer>, favorite: bool) {
viewer.favorite_suppress.set(true);
viewer.favorite.set_active(favorite);
@@ -59,7 +59,7 @@ pub(crate) fn show_favorite(viewer: &Rc<Viewer>, favorite: bool) {
viewer.favorite_suppress.set(false);
}
/// Filled star for a favourite, outline for the rest.
/// Filled star for a favorite, outline for the rest.
fn set_favorite_icon(button: &gtk4::ToggleButton, favorite: bool) {
button.set_icon_name(if favorite {
"starred-symbolic"
@@ -68,7 +68,7 @@ fn set_favorite_icon(button: &gtk4::ToggleButton, favorite: bool) {
});
}
/// Write a favourite change back into the loaded gallery page, so returning to
/// Write a favorite change back into the loaded gallery page, so returning to
/// the grid (or reopening the photo) shows what was just set without a reload.
pub(crate) fn set_gallery_favorite(ui: &Rc<Ui>, uid: &str, favorite: bool) {
let Some(idx) = find_photo_index(&ui.gallery.model, uid) else {
@@ -147,7 +147,7 @@ pub(crate) fn load_photo(ui: &Rc<Ui>, viewer: &Rc<Viewer>, uid: String) {
*viewer.path.borrow_mut() = None;
clear_info(viewer);
// The favourite state comes from the timeline page the gallery already has,
// The favorite state comes from the timeline page the gallery already has,
// so the button is right on the first frame instead of after a round-trip.
let favorite = find_photo_index(&ui.gallery.model, &uid)
.and_then(|idx| ui.gallery.model.item(idx))
@@ -556,7 +556,7 @@ pub(crate) fn open_photo_viewer(ui: &Rc<Ui>, initial_uid: String) {
let favorite_btn = gtk4::ToggleButton::builder()
.icon_name("non-starred-symbolic")
.tooltip_text("Favourite")
.tooltip_text("Favorite")
.valign(gtk4::Align::Center)
.build();
favorite_btn.add_css_class("flat");
@@ -758,7 +758,7 @@ pub(crate) fn open_photo_viewer(ui: &Rc<Ui>, initial_uid: String) {
_ => Some("The mount service didn't respond.".to_string()),
};
if let Some(detail) = failed {
toast_error(&ui_result, "Couldn't change the favourite", &detail);
toast_error(&ui_result, "Couldn't change the favorite", &detail);
// The server refused, so the button must go back to describing
// what is actually stored — without firing this handler again.
if *viewer_result.uid.borrow() == uid {
+16 -16
View File
@@ -84,7 +84,7 @@ pub(crate) struct GalleryState {
/// The capture-time window the timeline is currently filtered to, or `None`
/// for the whole span. Read by [`load_gallery`], set by the date dropdown.
pub(crate) range: Cell<Option<(i64, i64)>>,
/// The favourites toggle, and whether it is on. When on, the timeline is
/// The favorites toggle, and whether it is on. When on, the timeline is
/// restricted to photos carrying Proton's `Favorite` tag.
pub(crate) favorites_btn: gtk4::ToggleButton,
pub(crate) favorites: Cell<bool>,
@@ -495,12 +495,12 @@ pub(crate) fn build_gallery_page() -> (gtk4::Widget, GalleryWidgets) {
tab_group.append(btn);
}
// Favourites: a filter, not a tab — it cuts across Photos / Videos / Raw, so
// Favorites: a filter, not a tab — it cuts across Photos / Videos / Raw, so
// it stays outside the segmented control rather than becoming a fifth option
// that would silently drop the kind the user picked.
let favorites_btn = gtk4::ToggleButton::builder()
.icon_name("starred-symbolic")
.tooltip_text("Show only favourites")
.tooltip_text("Show only favorites")
.build();
favorites_btn.add_css_class("pill");
@@ -962,7 +962,7 @@ pub(crate) fn wire_gallery(
});
}
// Favourites: reload the timeline restricted to favourites (or back to all).
// Favorites: reload the timeline restricted to favorites (or back to all).
// Independent of the kind tabs and the date jump, both of which keep their
// current value across the toggle.
let ui_fav = ui.clone();
@@ -2118,7 +2118,7 @@ pub(crate) fn short_capture_time(secs: i64) -> String {
/// Fetch a timeline page from the daemon. When `append` is false the model is
/// cleared first (fresh load); otherwise the next page is tacked on.
/// The empty state for a timeline filtered to `kind`, favourites and/or a
/// The empty state for a timeline filtered to `kind`, favorites and/or a
/// `month`: what is missing, in the filter's own words.
pub(crate) fn empty_timeline_text(
kind: Option<PhotoKind>,
@@ -2138,12 +2138,12 @@ pub(crate) fn empty_timeline_text(
"Photos you upload to Proton Drive appear here.".to_string(),
),
(true, None, None) => (
"No favourites yet".to_string(),
"No favorites yet".to_string(),
"Star a photo in the viewer or from its menu to find it here.".to_string(),
),
(true, _, _) => (
format!("No favourite {what}{when}"),
"Turn off the favourites filter to see everything.".to_string(),
format!("No favorite {what}{when}"),
"Turn off the favorites filter to see everything.".to_string(),
),
(false, _, _) => (
format!("No {what}{when}"),
@@ -2222,9 +2222,9 @@ fn show_photo_menu(ui: &Rc<Ui>, photo: &PhotoItem, anchor: &gtk4::Button, x: f64
let (ui_c, uid, favorite) = (ui.clone(), photo.uid.clone(), photo.favorite);
item(
if favorite {
"Remove from Favourites"
"Remove from Favorites"
} else {
"Add to Favourites"
"Add to Favorites"
},
if favorite {
"non-starred-symbolic"
@@ -2269,18 +2269,18 @@ fn set_photo_favorite(ui: &Rc<Ui>, uid: String, favorite: bool) {
toast(
&ui,
if favorite {
"Added to Favourites"
"Added to Favorites"
} else {
"Removed from Favourites"
"Removed from Favorites"
},
);
}
Ok(Ok(Response::Error { message, .. })) => {
toast_error(&ui, "Couldn't change the favourite", &message)
toast_error(&ui, "Couldn't change the favorite", &message)
}
_ => toast_error(
&ui,
"Couldn't change the favourite",
"Couldn't change the favorite",
"The mount service didn't respond.",
),
}
@@ -2540,14 +2540,14 @@ mod tests {
#[test]
fn an_empty_filter_names_what_it_filtered() {
assert_eq!(empty_timeline_text(None, false, None).0, "No photos yet");
assert_eq!(empty_timeline_text(None, true, None).0, "No favourites yet");
assert_eq!(empty_timeline_text(None, true, None).0, "No favorites yet");
assert_eq!(
empty_timeline_text(Some(PhotoKind::Video), false, Some("June 2024")).0,
"No videos in June 2024"
);
assert_eq!(
empty_timeline_text(Some(PhotoKind::Raw), true, None).0,
"No favourite raw files"
"No favorite raw files"
);
}
}
+2 -6
View File
@@ -190,7 +190,7 @@ pub(crate) fn build_main_page() -> MainWidgets {
let appearance_group = adw::PreferencesGroup::builder().title("Appearance").build();
let accent_row = adw::SwitchRow::builder()
.title("Proton purple accent")
.subtitle("Use the Proton brand colour instead of the system accent colour")
.subtitle("Use the Proton brand color instead of the system accent color")
.build();
appearance_group.add(&accent_row);
@@ -531,11 +531,7 @@ pub(crate) fn wire_settings(
let mut config = ui_accent.dirs.load_config();
config.proton_accent = Some(on);
if let Err(e) = ui_accent.dirs.save_config(&config) {
toast_error(
&ui_accent,
"Couldn't save the accent colour",
&e.to_string(),
);
toast_error(&ui_accent, "Couldn't save the accent color", &e.to_string());
}
});
}