native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge upstream/main: Focus notification settings (#154)

onevcat 333ac99b 5cdaaf0b

+63 -37
+4 -28
supacode/Features/App/Reducer/AppFeature.swift
··· 72 72 enum Alert: Equatable { 73 73 case dismiss 74 74 case confirmQuit 75 - case openSystemNotificationSettings 76 75 } 77 76 78 77 @Dependency(AnalyticsClient.self) private var analyticsClient ··· 613 612 } 614 613 615 614 case .systemNotificationsPermissionFailed(let errorMessage): 616 - let message: String 617 - if let errorMessage, !errorMessage.isEmpty { 618 - message = 619 - "Prowl cannot send system notifications.\n\n" 620 - + "Error: \(errorMessage)" 621 - } else { 622 - message = "Prowl cannot send system notifications while permission is denied." 623 - } 624 - state.alert = AlertState { 625 - TextState("Enable Notifications in System Settings") 626 - } actions: { 627 - ButtonState(action: .openSystemNotificationSettings) { 628 - TextState("Open System Settings") 629 - } 630 - ButtonState(role: .cancel, action: .dismiss) { 631 - TextState("Cancel") 632 - } 633 - } message: { 634 - TextState(message) 635 - } 636 - return .send(.settings(.setSystemNotificationsEnabled(false))) 615 + return .concatenate( 616 + .send(.settings(.setSystemNotificationsEnabled(false))), 617 + .send(.settings(.showNotificationPermissionAlert(errorMessage: errorMessage))) 618 + ) 637 619 638 620 case .alert(.dismiss): 639 621 state.alert = nil 640 622 return .none 641 - 642 - case .alert(.presented(.openSystemNotificationSettings)): 643 - state.alert = nil 644 - return .run { _ in 645 - await systemNotificationClient.openSettings() 646 - } 647 623 648 624 case .alert(.presented(.confirmQuit)): 649 625 analyticsClient.capture("app_quit", nil)
+45
supacode/Features/Settings/Reducer/SettingsFeature.swift
··· 24 24 var defaultWorktreeBaseDirectoryPath: String 25 25 var selection: SettingsSection? = .general 26 26 var repositorySettings: RepositorySettingsFeature.State? 27 + @Presents var alert: AlertState<Alert>? 27 28 28 29 init(settings: GlobalSettings = .default) { 29 30 let normalizedDefaultEditorID = OpenWorktreeAction.normalizedDefaultEditorID(settings.defaultEditorID) ··· 77 78 case settingsLoaded(GlobalSettings) 78 79 case setSelection(SettingsSection?) 79 80 case setSystemNotificationsEnabled(Bool) 81 + case showNotificationPermissionAlert(errorMessage: String?) 80 82 case repositorySettings(RepositorySettingsFeature.Action) 83 + case alert(PresentationAction<Alert>) 81 84 case delegate(Delegate) 82 85 case binding(BindingAction<State>) 83 86 } 84 87 88 + enum Alert: Equatable { 89 + case dismiss 90 + case openSystemNotificationSettings 91 + } 92 + 85 93 @CasePathable 86 94 enum Delegate: Equatable { 87 95 case settingsChanged(GlobalSettings) 88 96 } 89 97 90 98 @Dependency(AnalyticsClient.self) private var analyticsClient 99 + @Dependency(SystemNotificationClient.self) private var systemNotificationClient 91 100 92 101 var body: some Reducer<State, Action> { 93 102 BindingReducer() ··· 148 157 defaultWorktreeBaseDirectoryPath 149 158 return persist(state) 150 159 160 + case .showNotificationPermissionAlert(let errorMessage): 161 + let message: String 162 + if let errorMessage, !errorMessage.isEmpty { 163 + message = 164 + "Prowl cannot send system notifications.\n\n" 165 + + "Error: \(errorMessage)" 166 + } else { 167 + message = "Prowl cannot send system notifications while permission is denied." 168 + } 169 + state.alert = AlertState { 170 + TextState("Enable Notifications in System Settings") 171 + } actions: { 172 + ButtonState(action: .openSystemNotificationSettings) { 173 + TextState("Open System Settings") 174 + } 175 + ButtonState(role: .cancel, action: .dismiss) { 176 + TextState("Cancel") 177 + } 178 + } message: { 179 + TextState(message) 180 + } 181 + return .none 182 + 151 183 case .setSelection(let selection): 152 184 state.selection = selection ?? .general 185 + return .none 186 + 187 + case .alert(.dismiss): 188 + state.alert = nil 189 + return .none 190 + 191 + case .alert(.presented(.openSystemNotificationSettings)): 192 + state.alert = nil 193 + return .run { _ in 194 + await systemNotificationClient.openSettings() 195 + } 196 + 197 + case .alert: 153 198 return .none 154 199 155 200 case .repositorySettings:
+2 -1
supacode/Features/Settings/Views/SettingsView.swift
··· 121 121 } 122 122 } 123 123 .navigationSplitViewStyle(.balanced) 124 + .alert(store: settingsStore.scope(state: \.$alert, action: \.alert)) 124 125 .alert(store: store.scope(state: \.$alert, action: \.alert)) 125 126 .frame(minWidth: 750, minHeight: 500) 126 127 .background { 127 128 WindowAppearanceSetter(colorScheme: settingsStore.appearanceMode.colorScheme) 128 - WindowLevelSetter(level: .floating) 129 + WindowLevelSetter(level: .normal) 129 130 } 130 131 .ignoresSafeArea(.container, edges: .top) 131 132 }
+12 -8
supacodeTests/AppFeatureSystemNotificationTests.swift
··· 35 35 await store.receive(\.settings.setSystemNotificationsEnabled) { 36 36 $0.settings.systemNotificationsEnabled = false 37 37 } 38 - 39 - let expectedAlert = AlertState<AppFeature.Alert> { 38 + let expectedAlert = AlertState<SettingsFeature.Alert> { 40 39 TextState("Enable Notifications in System Settings") 41 40 } actions: { 42 41 ButtonState(action: .openSystemNotificationSettings) { ··· 48 47 } message: { 49 48 TextState("Prowl cannot send system notifications.\n\nError: Mock request error") 50 49 } 50 + await store.receive(\.settings.showNotificationPermissionAlert) { 51 + $0.settings.alert = expectedAlert 52 + } 51 53 52 54 #expect(authorizationRequests.value == 1) 53 55 #expect(store.state.settings.systemNotificationsEnabled == false) 54 - #expect(store.state.alert == expectedAlert) 56 + #expect(store.state.settings.alert == expectedAlert) 55 57 } 56 58 57 59 @Test(.dependencies) func deniedStatusShowsAlertAndOpensSystemSettings() async { ··· 86 88 await store.receive(\.settings.setSystemNotificationsEnabled) { 87 89 $0.settings.systemNotificationsEnabled = false 88 90 } 89 - 90 - let expectedAlert = AlertState<AppFeature.Alert> { 91 + let expectedAlert = AlertState<SettingsFeature.Alert> { 91 92 TextState("Enable Notifications in System Settings") 92 93 } actions: { 93 94 ButtonState(action: .openSystemNotificationSettings) { ··· 99 100 } message: { 100 101 TextState("Prowl cannot send system notifications.\n\nError: Authorization status is denied.") 101 102 } 103 + await store.receive(\.settings.showNotificationPermissionAlert) { 104 + $0.settings.alert = expectedAlert 105 + } 102 106 103 107 #expect(authorizationRequests.value == 0) 104 108 #expect(store.state.settings.systemNotificationsEnabled == false) 105 - #expect(store.state.alert == expectedAlert) 109 + #expect(store.state.settings.alert == expectedAlert) 106 110 107 - await store.send(.alert(.presented(.openSystemNotificationSettings))) { 108 - $0.alert = nil 111 + await store.send(.settings(.alert(.presented(.openSystemNotificationSettings)))) { 112 + $0.settings.alert = nil 109 113 } 110 114 await store.finish() 111 115 #expect(openedSettings.value == 1)