-
Notifications
You must be signed in to change notification settings - Fork 0
Profileの名前を変更可能に #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cbdc0d1
3f2b510
7d24f09
1c46714
295a478
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -31,7 +31,8 @@ public partial class WindowItem : ObservableObject | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| public partial class ProfileItem : ObservableObject | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ObservableProperty] private bool _syncMinMax; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Name { get; init; } = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ObservableProperty] private string _name = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Id { get; init; } = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public int WindowCount { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -44,6 +45,7 @@ public partial class MainViewModel : ObservableObject | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly SyncManager _syncManager; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly AppSettingsStore _appSettings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly ILogger _log; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private bool _isUpdatingProfileName; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ObservableProperty] private string _statusText = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ObservableProperty] private string _profileName = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -143,6 +145,7 @@ private void SaveProfile() | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var profile = existing ?? new Profile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Id = Guid.NewGuid().ToString("D"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name = name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CreatedAt = now, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyncMinMax = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -239,7 +242,7 @@ private async Task ApplyProfile() | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| StatusText = "プロファイルを選択してください。"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await DoApplyAsync(SelectedProfile.Name, false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await DoApplyAsync(SelectedProfile.Id, false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [RelayCommand] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -250,20 +253,22 @@ private async Task LaunchAndApplyProfile() | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| StatusText = "プロファイルを選択してください。"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await DoApplyAsync(SelectedProfile.Name, true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await DoApplyAsync(SelectedProfile.Id, true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async Task DoApplyAsync(string profileName, bool launchMissing) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async Task DoApplyAsync(string profileId, bool launchMissing) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var profile = _store.FindByName(profileName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var profile = _store.FindById(profileId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (profile == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StatusText = $"プロファイルが見つかりません: {profileName}"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StatusText = $"プロファイルが見つかりません"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var profileName = profile.Name; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var candidates = GetCandidates(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int applied = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var failures = new List<string>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -405,7 +410,7 @@ private void DeleteProfile() | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_store.DeleteProfile(name)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_store.DeleteProfileById(SelectedProfile.Id)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ReloadProfiles(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _syncManager.ScheduleRebuild(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -424,29 +429,94 @@ public void ReloadProfiles() | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var item = new ProfileItem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Id = p.Id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name = p.Name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyncMinMax = p.SyncMinMax != 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WindowCount = p.Windows.Count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item.PropertyChanged += (s, e) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (e.PropertyName == nameof(ProfileItem.SyncMinMax) && s is ProfileItem pi) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (s is not ProfileItem pi) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (e.PropertyName == nameof(ProfileItem.SyncMinMax)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OnProfileSyncChanged(pi); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (e.PropertyName == nameof(ProfileItem.Name)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OnProfileNameChanged(pi); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Profiles.Add(item); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void OnProfileNameChanged(ProfileItem pi) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Prevent re-entrancy when we programmatically update pi.Name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_isUpdatingProfileName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get the current profile from the store | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var current = _store.FindById(pi.Id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (current == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StatusText = "プロファイルが見つかりません。"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If the name has not actually changed compared to the store, ignore. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.Equals(current.Name, pi.Name, StringComparison.Ordinal)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var newName = pi.Name?.Trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(newName)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Revert to current stored name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _isUpdatingProfileName = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pi.Name = current.Name; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
468
to
475
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var newName = pi.Name?.Trim(); | |
| if (string.IsNullOrEmpty(newName)) | |
| { | |
| // Revert to current stored name | |
| var current = _store.FindById(pi.Id); | |
| if (current != null) | |
| pi.Name = current.Name; | |
| // Get the current profile from the store | |
| var current = _store.FindById(pi.Id); | |
| if (current == null) | |
| { | |
| StatusText = "プロファイルが見つかりません。"; | |
| return; | |
| } | |
| // If the name has not actually changed compared to the store, ignore. | |
| if (string.Equals(current.Name, pi.Name)) | |
| { | |
| return; | |
| } | |
| var newName = pi.Name?.Trim(); | |
| if (string.IsNullOrEmpty(newName)) | |
| { | |
| // Revert to current stored name | |
| pi.Name = current.Name; |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When finalName != newName, assigning pi.Name = finalName will re-trigger OnProfileNameChanged and call _store.RenameProfile a second time, resulting in duplicate saves and timestamp changes. Add a re-entrancy guard or a check against the store's current name before calling rename/setting the property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When sync is disabled in
UpdateHooksIfNeeded,_syncGroupsand_lastMinMaxByHwndare cleared but_profileNamesis left as-is. This can leave stale name mappings around longer than needed; consider clearing_profileNamesalongside_syncGroupsfor consistency.