Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Sources/Extensions/Value+Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ extension Value {
case .int:
return "number.square"
case .number:
return "number.square.fill"
return "function"
case .string:
return "textformat"
case .secure:
return "eye.slash"
return "lock.fill"
case .object:
return "aqi.medium"
return "curlybraces"
}
}
}
28 changes: 24 additions & 4 deletions Sources/ToggleManager/ToggleManager+Overrides.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,40 @@ extension ToggleManager {
}
}

/// Returns whether the given variable has an active override.
/// - Parameter variable: The variable to check.
/// - Returns: `true` if the variable is overridden, `false` otherwise.
func isOverridden(_ variable: Variable) -> Bool {
queue.sync {
guard let mutableValueProvider = mutableValueProvider else { return false }
return mutableValueProvider.value(for: variable) != nil
}
}

/// Removes all overridden toggles in the mutable value provider, if one was provided during initialization.
@discardableResult
public func removeOverrides() -> Set<Variable> {
queue.sync(flags: .barrier) {
guard let mutableValueProvider = mutableValueProvider else { return [] }
let variables = mutableValueProvider.variables
log("Deleting all overrides.")
mutableValueProvider.deleteAll()

// Clear cache for all variables that had overrides
for variable in variables {
cache[variable] = nil
}

// Send updated values to existing subjects
for variable in variables {
DispatchQueue.main.async {
self.subjectsRefs[variable]?.send(completion: .finished)
self.subjectsRefs[variable] = nil
if let subject = self.subjectsRefs[variable] {
let newValue = self.value(for: variable)
subject.send(newValue)
}
}
}
log("Deleting all overrides.")
mutableValueProvider.deleteAll()

hasOverrides = false
return variables
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/ToggleManager/ToggleManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ extension ToggleManager {
self.cache[variable] = nil
mutableValueProvider.delete(variable)
DispatchQueue.main.async {
self.subjectsRefs[variable]?.send(completion: .finished)
self.subjectsRefs[variable] = nil
// Send the new default value to the subject
if let subject = self.subjectsRefs[variable] {
let newValue = self.value(for: variable)
subject.send(newValue)
}
self.hasOverrides = !mutableValueProvider.variables.isEmpty
}
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/Utilities/InputValidationHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ struct InputValidationHelper {
return false
}

var isObjectToggle: Bool {
if case .object = toggle.value {
return true
}
return false
}

var toggleNeedsValidation: Bool {
if case .bool = toggle.value { return false }
if case .string = toggle.value { return false }
Expand Down
201 changes: 0 additions & 201 deletions Sources/Views/ToggleDetailView.swift

This file was deleted.

94 changes: 94 additions & 0 deletions Sources/Views/ToggleDetailView/BooleanOverrideControl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// BooleanOverrideControl.swift

import SwiftUI

struct BooleanOverrideControl: View {
let manager: ToggleManager
let toggle: Toggle

@Binding var boolValue: Bool
@Binding var valueOverridden: Bool

private var isOverridden: Bool {
manager.isOverridden(toggle.variable)
}

var body: some View {
HStack {
Text("Override to")
.foregroundStyle(.secondary)

Spacer()

HStack(spacing: 12) {
Button {
manager.set(.bool(false), for: toggle.variable)
boolValue = false
showOverrideFeedback()
} label: {
Text("false")
.font(.system(.body, design: .monospaced))
.fontWeight(.medium)
.foregroundStyle(!boolValue && isOverridden ? .white : .red)
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(!boolValue && isOverridden ? Color.red : Color.red.opacity(0.15))
.clipShape(RoundedRectangle(cornerRadius: 8))
}
.buttonStyle(.plain)

Button {
manager.set(.bool(true), for: toggle.variable)
boolValue = true
showOverrideFeedback()
} label: {
Text("true")
.font(.system(.body, design: .monospaced))
.fontWeight(.medium)
.foregroundStyle(boolValue && isOverridden ? .white : .green)
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(boolValue && isOverridden ? Color.green : Color.green.opacity(0.15))
.clipShape(RoundedRectangle(cornerRadius: 8))
}
.buttonStyle(.plain)
}
}
}

private func showOverrideFeedback() {
valueOverridden = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
valueOverridden = false
}
}
}

private struct BooleanOverrideControlPreview: View {
@State private var boolValue = true
@State private var valueOverridden = false

var body: some View {
let datasourceUrl = Bundle.module.url(forResource: "PreviewDatasource", withExtension: "json")!
let manager = try! ToggleManager(mutableValueProvider: InMemoryValueProvider(),
datasourceUrl: datasourceUrl)
let content = try! Data(contentsOf: datasourceUrl)
let datasource = try! JSONDecoder().decode(Datasource.self, from: content)
let boolToggle = datasource.toggles.first { toggle in
if case .bool = toggle.value { return true }
return false
}!
List {
BooleanOverrideControl(
manager: manager,
toggle: boolToggle,
boolValue: $boolValue,
valueOverridden: $valueOverridden
)
}
}
}

#Preview("BooleanOverrideControl") {
BooleanOverrideControlPreview()
}
Loading