From 4e95a09706b1da64eacd9d680b4d8b1b3f661b1c Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 16 Oct 2026 11:18:28 -0400 Subject: [PATCH] feat(desktop): ask for a new nest name on database Save As Database Save As uploaded the copy under the same name as the original, so the saved nests list showed two indistinguishable entries. It now prompts for the copy's name first (prefilled with the current one) and cancelling aborts the save. The prompt is skipped while a lost upload is being reconciled, since that click never writes. If the upload fails outright the original name is restored, so a later Save cannot rename the original nest; it is kept only while the copy may still be pending. Co-Authored-By: Claude Opus 5.5 --- OpenNest/Forms/MainForm.cs | 26 ++++++++++-- OpenNest/Forms/NestNameForm.cs | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 OpenNest/Forms/NestNameForm.cs diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index 5c911259..73b4e6b6 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -884,13 +884,27 @@ namespace OpenNest.Forms if (form == null || databaseSaveInProgress) return; if (NestStorage.Settings.Mode == NestStorageMode.File) + { form.SaveAs(); - else - await SaveDatabaseAsync(form, saveCopy: true); + return; + } + + // A pending upload is reconciled without writing, so only ask for a name + // when this click will actually create the copy. + string copyName = null; + if (!form.Document.PendingCreateRequiresReconciliation) + { + using var dlg = new NestNameForm(form.Nest.Name); + if (dlg.ShowDialog(this) != DialogResult.OK) + return; + copyName = dlg.NestName; + } + await SaveDatabaseAsync(form, saveCopy: true, copyName); } - private async Task SaveDatabaseAsync(EditNestForm form, bool saveCopy) + private async Task SaveDatabaseAsync(EditNestForm form, bool saveCopy, string copyName = null) { + var originalName = form.Nest.Name; databaseSaveInProgress = true; storageModeMenu.Enabled = false; mnuToolsOptions.Enabled = false; @@ -908,6 +922,8 @@ namespace OpenNest.Forms : $"Found saved version {reconciled.Revision}. Review the current nest before saving again."; return; // Never combine reconciliation with a write in the same click. } + if (copyName != null) + form.Nest.Name = copyName; var saved = await form.Document.SaveToDatabaseAsync( repository, serverUrl, saveCopy); if (!form.IsDisposed) @@ -917,6 +933,10 @@ namespace OpenNest.Forms } catch (Exception ex) { + // Keep the new name only while its copy may still be pending on the server; + // otherwise a later Save would rename the original nest. + if (copyName != null && !form.Document.PendingCreateWasCopy) + form.Nest.Name = originalName; if (!IsDisposed) ShowStorageError("save", ex); } diff --git a/OpenNest/Forms/NestNameForm.cs b/OpenNest/Forms/NestNameForm.cs new file mode 100644 index 00000000..e746b80c --- /dev/null +++ b/OpenNest/Forms/NestNameForm.cs @@ -0,0 +1,76 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Forms; + +/// +/// Database-mode Save As: asks for the name of the new nest copy, since there is no +/// file dialog to supply one. +/// +public sealed class NestNameForm : Form +{ + private readonly TextBox nameBox; + + public NestNameForm(string currentName) + { + Text = "Save As"; + StartPosition = FormStartPosition.CenterParent; + ClientSize = new Size(380, 110); + MinimizeBox = false; + MaximizeBox = false; + ShowInTaskbar = false; + FormBorderStyle = FormBorderStyle.FixedDialog; + + var label = new Label + { + Text = "New nest name:", + AutoSize = true, + Location = new Point(12, 16), + }; + nameBox = new TextBox + { + Location = new Point(12, 36), + Width = 356, + Text = currentName ?? "", + }; + + var buttons = new FlowLayoutPanel + { + Dock = DockStyle.Bottom, + Height = 44, + FlowDirection = FlowDirection.RightToLeft, + Padding = new Padding(6), + }; + var cancel = new Button { Text = "Cancel", AutoSize = true, DialogResult = DialogResult.Cancel }; + var ok = new Button { Text = "Save", AutoSize = true }; + ok.Click += Ok_Click; + buttons.Controls.Add(cancel); + buttons.Controls.Add(ok); + + Controls.Add(nameBox); + Controls.Add(label); + Controls.Add(buttons); + + AcceptButton = ok; + CancelButton = cancel; + + Shown += (_, _) => nameBox.SelectAll(); + } + + public string NestName => nameBox.Text.Trim(); + + private void Ok_Click(object sender, EventArgs e) + { + if (NestName.Length == 0) + { + MessageBox.Show(this, "Enter a name for the new nest.", Text, + MessageBoxButtons.OK, MessageBoxIcon.Warning); + nameBox.Focus(); + return; + } + + DialogResult = DialogResult.OK; + Close(); + } +}