From ac67da1f350f8dcdea6c0ae804b8d5a03ae92e0f Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:03:14 -0600 Subject: [PATCH 1/7] add a popover confirming whether to replace or merge data --- beeps/src/app/popover.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/beeps/src/app/popover.rs b/beeps/src/app/popover.rs index 75acc49..d62d7c4 100644 --- a/beeps/src/app/popover.rs +++ b/beeps/src/app/popover.rs @@ -19,6 +19,9 @@ pub enum Popover { /// Register with the server Authenticating(auth_form::AuthForm, AuthIntent), + + /// Confirm whether or not we want to replace the full sync information or merge it. + ConfirmReplaceOrMerge, } /// When we're working with an Authenticating popover, what do we intend to do @@ -103,6 +106,28 @@ impl Popover { )); } Popover::Authenticating(auth, _) => auth.render(body_area, frame), + Popover::ConfirmReplaceOrMerge => { + let popup_vert = Layout::vertical([Constraint::Percentage(50)]).flex(Flex::Center); + let popup_horiz = + Layout::horizontal([Constraint::Percentage(50)]).flex(Flex::Center); + + let [popup_area] = popup_vert.areas(body_area); + let [popup_area] = popup_horiz.areas(popup_area); + + let popup = Paragraph::new( + "You've successfully logged in! Do you want to replace your local data with the server's data, or merge local and remote data?\n\nPress 'r' to replace, or 'm' to merge.", + ) + .block( + Block::default() + .borders(Borders::ALL) + .title("Replace or Merge?") + .padding(Padding::horizontal(1)) + .border_style(Style::new().blue()), + ); + + frame.render_widget(Clear, popup_area); + frame.render_widget(popup, popup_area); + } } } } From 7943fd54d5778a4d85fe9ab0a1cec17752bdecd8 Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:11:46 -0600 Subject: [PATCH 2/7] ask whether to replace or merge --- beeps/src/app.rs | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/beeps/src/app.rs b/beeps/src/app.rs index b062488..95bff8d 100644 --- a/beeps/src/app.rs +++ b/beeps/src/app.rs @@ -16,7 +16,7 @@ use popover::{AuthIntent, Popover}; use crate::config::Config; use beeps_core::{ sync::{login, register, Client}, - NodeId, Replica, + Document, NodeId, Replica, }; use chrono::{DateTime, Local, Utc}; use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; @@ -42,7 +42,12 @@ pub struct App { /// If we're replacing the entire replica on the next sync (for example when /// initially logging in.) - replace_on_next_sync: bool, + in_first_sync: bool, + + /// The document we got on our first sync. We keep this separate to decide + /// whether to replace our current document with this or merge them + /// together. + first_sync_document: Option, /// State of the pings table table_state: TableState, @@ -89,7 +94,8 @@ impl App { Ok(Self { status_line: Some("Loaded replica".to_string()), replica, - replace_on_next_sync: false, + in_first_sync: false, + first_sync_document: None, client: auth, last_sync: None, table_state: TableState::new().with_selected(0), @@ -104,7 +110,8 @@ impl App { status_line: None, replica: Replica::new(NodeId::random()), client: auth, - replace_on_next_sync: false, + in_first_sync: false, + first_sync_document: None, last_sync: None, table_state: TableState::new().with_selected(0), popover: None, @@ -253,7 +260,7 @@ impl App { } Action::LoggedIn(client) => { self.client = Some(client.clone()); - self.replace_on_next_sync = true; + self.in_first_sync = true; vec![ Effect::SaveSyncClientAuth(client.clone()), @@ -273,12 +280,13 @@ impl App { Action::Pulled(resp) => { self.status_line = Some("Got a new doc from the server".to_string()); - if self.replace_on_next_sync { - self.replica.replace_doc(resp.document); - self.replace_on_next_sync = false; + if self.in_first_sync { + self.in_first_sync = false; + self.first_sync_document = Some(resp.document); + self.popover = Some(Popover::ConfirmReplaceOrMerge); } else { self.replica.merge(resp.document); - } + }; vec![] } @@ -355,6 +363,25 @@ impl App { } _ => auth.handle_event(key), }, + Some(Popover::ConfirmReplaceOrMerge) => match key.code { + KeyCode::Char('r') => { + self.dismiss_popover(); + self.in_first_sync = true; + if let Some(document) = self.first_sync_document.take() { + self.replica.replace_doc(document); + effects.push(Effect::SaveReplica(self.replica.clone())); + } + } + KeyCode::Char('m') => { + self.dismiss_popover(); + self.in_first_sync = false; + if let Some(document) = self.first_sync_document.take() { + self.replica.merge(document); + effects.push(Effect::SaveReplica(self.replica.clone())); + } + } + _ => (), + }, } effects From 9de84af14219bad3685a2d71287585d1624f2ca6 Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:29:42 -0600 Subject: [PATCH 3/7] don't set first sync when handling popover result --- beeps/src/app.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/beeps/src/app.rs b/beeps/src/app.rs index 95bff8d..41ea3ea 100644 --- a/beeps/src/app.rs +++ b/beeps/src/app.rs @@ -366,7 +366,6 @@ impl App { Some(Popover::ConfirmReplaceOrMerge) => match key.code { KeyCode::Char('r') => { self.dismiss_popover(); - self.in_first_sync = true; if let Some(document) = self.first_sync_document.take() { self.replica.replace_doc(document); effects.push(Effect::SaveReplica(self.replica.clone())); @@ -374,7 +373,6 @@ impl App { } KeyCode::Char('m') => { self.dismiss_popover(); - self.in_first_sync = false; if let Some(document) = self.first_sync_document.take() { self.replica.merge(document); effects.push(Effect::SaveReplica(self.replica.clone())); From 0cc053ddc3dc84e7b121d678354ab93430e7ba3a Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:35:05 -0600 Subject: [PATCH 4/7] don't replace/merge on the second sync if we take a little bit --- beeps/src/app.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beeps/src/app.rs b/beeps/src/app.rs index 41ea3ea..12945eb 100644 --- a/beeps/src/app.rs +++ b/beeps/src/app.rs @@ -281,7 +281,6 @@ impl App { self.status_line = Some("Got a new doc from the server".to_string()); if self.in_first_sync { - self.in_first_sync = false; self.first_sync_document = Some(resp.document); self.popover = Some(Popover::ConfirmReplaceOrMerge); } else { @@ -366,6 +365,7 @@ impl App { Some(Popover::ConfirmReplaceOrMerge) => match key.code { KeyCode::Char('r') => { self.dismiss_popover(); + self.in_first_sync = false; if let Some(document) = self.first_sync_document.take() { self.replica.replace_doc(document); effects.push(Effect::SaveReplica(self.replica.clone())); @@ -373,6 +373,7 @@ impl App { } KeyCode::Char('m') => { self.dismiss_popover(); + self.in_first_sync = false; if let Some(document) = self.first_sync_document.take() { self.replica.merge(document); effects.push(Effect::SaveReplica(self.replica.clone())); From 15c36c973c011296e715f9c906ed97118904ee97 Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:37:15 -0600 Subject: [PATCH 5/7] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25880f9..9778c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.5.2 (Unreleased) +- Logging in no longer replaces your document. Instead, it asks if you'd like to merge or replace. + +This release is available as a container image at `ghcr.io/bytes-zone/beeps:v0.5.2`. + ## 0.5.1 (2025-01-28) - Fixes a missing dependency in the container image. From 5419de3ca68c693e631afb3f5adeb30aafc00f0d Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:37:47 -0600 Subject: [PATCH 6/7] bump to 0.5.2 --- Cargo.lock | 8 ++++---- beeps-server/Cargo.toml | 2 +- beeps/Cargo.toml | 2 +- beeps_core/Cargo.toml | 2 +- browser/Cargo.toml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c57c217..4f6e817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,7 +427,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beeps" -version = "0.5.1" +version = "0.5.2" dependencies = [ "beeps_core", "chrono", @@ -449,7 +449,7 @@ dependencies = [ [[package]] name = "beeps-server" -version = "0.5.1" +version = "0.5.2" dependencies = [ "argon2", "axum", @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "beeps_core" -version = "0.5.1" +version = "0.5.2" dependencies = [ "chrono", "proptest", @@ -568,7 +568,7 @@ dependencies = [ [[package]] name = "browser" -version = "0.5.1" +version = "0.5.2" dependencies = [ "beeps_core", "chrono", diff --git a/beeps-server/Cargo.toml b/beeps-server/Cargo.toml index 7ee1bf0..6b9e3fc 100644 --- a/beeps-server/Cargo.toml +++ b/beeps-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "beeps-server" -version = "0.5.1" +version = "0.5.2" edition = "2021" [dependencies] diff --git a/beeps/Cargo.toml b/beeps/Cargo.toml index 7103741..f008909 100644 --- a/beeps/Cargo.toml +++ b/beeps/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "beeps" -version = "0.5.1" +version = "0.5.2" edition = "2021" [lints] diff --git a/beeps_core/Cargo.toml b/beeps_core/Cargo.toml index 33f2431..158bb82 100644 --- a/beeps_core/Cargo.toml +++ b/beeps_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "beeps_core" -version = "0.5.1" +version = "0.5.2" edition = "2021" [lints] diff --git a/browser/Cargo.toml b/browser/Cargo.toml index 7b4cea5..de5a277 100644 --- a/browser/Cargo.toml +++ b/browser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "browser" -version = "0.5.1" +version = "0.5.2" edition = "2021" [lib] From ee659d0cf7c245c28b94199490a18d7a112c2d6c Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 28 Jan 2025 06:38:05 -0600 Subject: [PATCH 7/7] mark 0.5.2 release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9778c9a..340baa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.2 (Unreleased) +## 0.5.2 (2025-01-28) - Logging in no longer replaces your document. Instead, it asks if you'd like to merge or replace.