Remembered lists are global; add Clear buttons in Preferences
Remembered peers were already machine-wide for the main app; remembered apps are now global too (a shared address book across all profiles), so it doesn't confuse the user with a different list per profile. - RemSoundSettingsStore.Load/SaveRememberedApplications (global, NOT written into profile files) mirrors remembered peers. Removed the per-profile Profile.RememberedSendApplications. Ticking an app in any profile adds it to the global list (RememberCheckedApps); the per-profile SelectedSendApplications (the active/ticked subset) is unchanged. - Preferences -> General now has two buttons at the end, under a 'Remembered lists (shared across all profiles)' header: 'Clear remembered peers list...' and 'Clear remembered applications list...', each with a Yes/No confirmation. Clearing peers refreshes the main window; clearing apps empties the global list. - Self-test 'Remembered applications list is global + clearable' (round-trip, case-insensitive dedupe, clear); the dialog accessibility audit covers the two new buttons (names + mnemonics, no clash). Gate 37/37. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1e5030b08f
commit
e95fc36926
@@ -2912,6 +2912,18 @@ public sealed class MainForm : Form
|
||||
checkForUpdatesNow: () => CheckForUpdatesManually(),
|
||||
onUpdateFrequencyChanged: ApplyUpdateCheckTimer,
|
||||
onAutoSaveIntervalChanged: ApplyAutoSaveTimer,
|
||||
onClearRememberedPeers: () =>
|
||||
{
|
||||
settings.SaveRememberedPeers(Array.Empty<string>());
|
||||
rememberedPeerInstanceIds.Clear();
|
||||
RefreshKnownPeers();
|
||||
logFile.Event("remembered peers list cleared (Preferences)");
|
||||
},
|
||||
onClearRememberedApplications: () =>
|
||||
{
|
||||
settings.SaveRememberedApplications(Array.Empty<string>());
|
||||
logFile.Event("remembered applications list cleared (Preferences)");
|
||||
},
|
||||
applyUpnpEnabled: enabled =>
|
||||
{
|
||||
// The persist already happened in the dialog; this callback only flips the
|
||||
@@ -3612,6 +3624,7 @@ public sealed class MainForm : Form
|
||||
if (suppressSendAppEvents) return;
|
||||
MarkProfileDirty();
|
||||
ApplySendSources();
|
||||
RememberCheckedApps();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3802,6 +3815,21 @@ public sealed class MainForm : Form
|
||||
|
||||
ApplySendModeVisibility();
|
||||
if (sendModeList.SelectedIndex == SendModeApplicationsIndex) ReconcileSendAppsList();
|
||||
RememberCheckedApps();
|
||||
}
|
||||
|
||||
/// <summary>Add the currently-ticked app names to the GLOBAL remembered-applications list (the shared
|
||||
/// "apps I send" address book) — so a tick in any profile remembers the app for all of them, mirroring
|
||||
/// how remembered peers work. Cleared from Preferences → General.</summary>
|
||||
private void RememberCheckedApps()
|
||||
{
|
||||
var names = CheckedSendApplicationNames();
|
||||
if (names.Count == 0) return;
|
||||
var remembered = settings.LoadRememberedApplications().ToList();
|
||||
var added = false;
|
||||
foreach (var n in names)
|
||||
if (!remembered.Any(e => string.Equals(e, n, StringComparison.OrdinalIgnoreCase))) { remembered.Add(n); added = true; }
|
||||
if (added) settings.SaveRememberedApplications(remembered);
|
||||
}
|
||||
|
||||
/// <summary>The process names the user has ticked in the app list (lower-case, no extension).</summary>
|
||||
|
||||
@@ -50,6 +50,21 @@ internal sealed class PreferencesDialog : Form
|
||||
// Parallel to autoSaveList.Items: the minute interval each row means (0 = Never).
|
||||
private static readonly int[] AutoSaveMinuteOptions = { 0, 2, 5, 10, 15, 20, 30 };
|
||||
|
||||
// Clear the GLOBAL (machine-wide) remembered lists — peers and applications are a shared address book
|
||||
// across all profiles, so these live in Preferences, not per-profile. Each asks for confirmation first.
|
||||
private readonly Button clearRememberedPeersButton = new()
|
||||
{
|
||||
Text = "Clear remembered &peers list...",
|
||||
AccessibleName = "Clear remembered peers list",
|
||||
AutoSize = true,
|
||||
};
|
||||
private readonly Button clearRememberedAppsButton = new()
|
||||
{
|
||||
Text = "Clear remembered applications &list...",
|
||||
AccessibleName = "Clear remembered applications list",
|
||||
AutoSize = true,
|
||||
};
|
||||
|
||||
/// <summary>Test seam: the auto-save interval rows (minutes; 0 = Never), so a self-test can assert the
|
||||
/// list Ed asked for stays intact.</summary>
|
||||
internal static IReadOnlyList<int> AutoSaveMinuteOptionsForTest => AutoSaveMinuteOptions;
|
||||
@@ -490,6 +505,8 @@ internal sealed class PreferencesDialog : Form
|
||||
Action checkForUpdatesNow,
|
||||
Action onUpdateFrequencyChanged,
|
||||
Action onAutoSaveIntervalChanged,
|
||||
Action onClearRememberedPeers,
|
||||
Action onClearRememberedApplications,
|
||||
Action<bool> applyUpnpEnabled,
|
||||
Func<(RouterMappingStatus Status, IPEndPoint? External, string LastError)> getUpnpSnapshot,
|
||||
Action<EventHandler> subscribeUpnpStatusChanged,
|
||||
@@ -657,6 +674,21 @@ internal sealed class PreferencesDialog : Form
|
||||
};
|
||||
autoSaveLabel.Click += (_, _) => autoSaveList.Focus();
|
||||
|
||||
clearRememberedPeersButton.Click += (_, _) =>
|
||||
{
|
||||
if (MessageBox.Show(this,
|
||||
"Clear the whole remembered peers list?\n\nThis empties the shared list of peers RemSound has remembered, for every profile. Peers you're actively connected to aren't affected, and any peer will simply be remembered again next time you connect to it.",
|
||||
"RemSound", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
|
||||
onClearRememberedPeers();
|
||||
};
|
||||
clearRememberedAppsButton.Click += (_, _) =>
|
||||
{
|
||||
if (MessageBox.Show(this,
|
||||
"Clear the whole remembered applications list?\n\nThis empties the shared list of applications RemSound has remembered to send, for every profile. Apps you're actively sending aren't affected, and an app is remembered again the next time you tick it.",
|
||||
"RemSound", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
|
||||
onClearRememberedApplications();
|
||||
};
|
||||
|
||||
// Update settings — wired against AppConfig directly since they're machine-local.
|
||||
// The frequency combo's index maps 1:1 to the UpdateCheckFrequency enum so reordering
|
||||
// either side stays in lockstep.
|
||||
@@ -990,8 +1022,12 @@ internal sealed class PreferencesDialog : Form
|
||||
};
|
||||
autoSavePanel.Controls.Add(autoSaveLabel);
|
||||
autoSavePanel.Controls.Add(autoSaveList);
|
||||
// The two "clear remembered list" actions sit at the END of the General tab (they're machine-wide
|
||||
// maintenance, not a per-profile setting). A header separates them from the settings above.
|
||||
var clearListsHeader = Theme.SectionHeader("Remembered lists (shared across all profiles)");
|
||||
tabs.TabPages.Add(MakeTab("General",
|
||||
browseProfilesFolderButton, autoSavePanel, acceptRemoteVolumeBox, upnpEnabledBox, upnpStatusLabel));
|
||||
browseProfilesFolderButton, autoSavePanel, acceptRemoteVolumeBox, upnpEnabledBox, upnpStatusLabel,
|
||||
clearListsHeader, clearRememberedPeersButton, clearRememberedAppsButton));
|
||||
tabs.TabPages.Add(MakeTab("Appearance",
|
||||
themeRow, showPanEqTabBox, tabOrderLabel, tabOrderList, tabOrderButtons,
|
||||
enableDiscoveredPeersBox, enableRememberedPeersBox));
|
||||
|
||||
@@ -69,6 +69,7 @@ internal static class SelfTest
|
||||
RunStep(results, "Service network presence (reachable + shell teardown)", ServiceNetworkPresenceReachable);
|
||||
RunStep(results, "Service reachability-gated sending (drop dead peers, re-arm recovered)", ServiceReachabilityGating);
|
||||
RunStep(results, "Send-app capture change-detection (catch an app the instant it opens)", SendAppCaptureChangeDetection);
|
||||
RunStep(results, "Remembered applications list is global + clearable", RememberedApplicationsGlobal);
|
||||
RunStep(results, "Service registration args", ServiceRegistrationArgs);
|
||||
RunStep(results, "Recording engine (all formats + source gate + mono)", RecordingEngine);
|
||||
RunStep(results, "Recording split tracks (per-peer + own)", RecordingSplitTracks);
|
||||
@@ -1116,7 +1117,7 @@ internal static class SelfTest
|
||||
("Recording settings", () => new RecordingSettingsDialog(new RecordingSettings())),
|
||||
("Preferences", () => new PreferencesDialog(
|
||||
new RemSoundSettingsStore("RemSound"), null,
|
||||
() => false, _ => { }, () => { }, () => 0, () => { }, () => { }, () => { }, _ => { },
|
||||
() => false, _ => { }, () => { }, () => 0, () => { }, () => { }, () => { }, () => { }, () => { }, _ => { },
|
||||
() => (default(RouterMappingStatus), (IPEndPoint?)null, ""),
|
||||
_ => { }, _ => { })),
|
||||
("Service profile", () => new ServiceProfileDialog(RemSound.Core.Profile.NewBlank(), false)),
|
||||
@@ -1551,6 +1552,27 @@ internal static class SelfTest
|
||||
return "signature stable when unchanged; changes when a ticked app opens/closes (drives instant capture)";
|
||||
}
|
||||
|
||||
/// <summary>Remembered applications are a GLOBAL, machine-wide list (like remembered peers), not
|
||||
/// per-profile — a shared "apps I send" address book — and can be cleared (the Preferences button).
|
||||
/// Verifies the store round-trips, dedupes case-insensitively to lower-case, and clears.</summary>
|
||||
private static string? RememberedApplicationsGlobal()
|
||||
{
|
||||
var store = new RemSoundSettingsStore("RemSound");
|
||||
var original = store.LoadRememberedApplications().ToList();
|
||||
try
|
||||
{
|
||||
store.SaveRememberedApplications(new[] { "VLC", "Firefox", "vlc" });
|
||||
var loaded = store.LoadRememberedApplications();
|
||||
Check(loaded.Count == 2, $"remembered apps must dedupe case-insensitively (got {loaded.Count})");
|
||||
Check(loaded.All(a => a == a.ToLowerInvariant()), "remembered app names must be stored lower-case");
|
||||
|
||||
store.SaveRememberedApplications(Array.Empty<string>());
|
||||
Check(store.LoadRememberedApplications().Count == 0, "clearing must empty the remembered applications list");
|
||||
return "global remembered applications: round-trip, case-insensitive dedupe, and clear";
|
||||
}
|
||||
finally { store.SaveRememberedApplications(original); }
|
||||
}
|
||||
|
||||
private static int FreeUdpPort()
|
||||
{
|
||||
using var s = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork,
|
||||
|
||||
Reference in New Issue
Block a user