uttrflow / uttrflow/uttrflow-swift

[P2] Settings edits overwrite newer choices made in the menu bar or main window

Open
#809 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

The Settings window retains a settings snapshot for its entire lifetime. Changes made through another screen are saved separately, so a later unrelated edit in Settings writes the old snapshot back and silently reverses those choices.

### Reproduction

1. With Suggestions enabled, open Settings once, then close it.
2. Disable Suggestions through the menu bar.
3. Reopen Settings and change Appearance to Dark.
4. Suggestions is enabled again, although the user only changed appearance.

The same stale-value mechanism applies to other settings changed through the main window while this controller remains alive.

### Evidence

Reviewed main at `533e6cbef5046f8bd681246d99ebf9ef9553fa40`:

- `Sources/Uttrflow/Settings/SettingsViewModel.swift`: initialization loads `store.load()` once; `apply` edits `session.settings`, and `persist` saves the entire resulting Settings value.
- `Sources/Uttrflow/Settings/SettingsWindowController.swift`: the controller retains one model; `show` refreshes identity, tab, personalisation and capabilities but never reloads the saved settings.
- `Sources/Uttrflow/AppDelegate.swift:1280`: the menu Suggestions switch calls `apply(.toggle(.suggestionsEnabled, isOn: isOn))`. The shared `apply` at line 1632 edits AppDelegate's settings, saves the result, and calls `settingsChanged`. That method updates app services and main-window presentation but does not synchronize the Settings model. Main-window `.change` actions use the same path at line 1552.

An isolated Swift test compiled the unmodified production SettingsViewModel, SettingsEditor and UserDefaultsSettingsStore, with an in-memory KeyValueStore. After creating the model with Suggestions enabled, it saved the menu-bar change through SettingsEditor and verified the stored value was false. Calling `model.apply(.appearance(.dark))` then produced `suggestions=true, appearance=dark`: the preservation assertion failed. A control that creates a fresh model after the external change passes.

This validates the production state/persistence path; the native window and menu clicks were not driven in this test.

### Expected fix and verification

Keep the Settings model synchronized with authoritative settings changes, including changes made while its window is open. Ensure a Settings edit cannot persist an obsolete value for unrelated fields. Add regression coverage for an external change followed by a Settings edit, both with the window reopened and still open, and verify existing settings callbacks remain functional.

Contributor guide

Open the contributing guide

Research direction

Start with Sources/Uttrflow/Settings/SettingsViewModel.swift and SettingsWindowController.swift, then trace AppDelegate.swift at lines 1280, 1552, and 1632 to understand the shared settings path. Reproduce the stale-value failure with the isolated Swift test and add regression coverage for external changes while Settings is reopened and still open. Done means unrelated settings are preserved and existing callbacks still work.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
desktop, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.