More improvements to unstable features and advanced settings

- added description of what they are and notice if there are none available
- move them to advanced settings
- add alert for unstable features in dev mode if the build does not have them enabled
- move stuff out of the danger zone and into anisette section in advanced settings
This commit is contained in:
naturecodevoid
2023-05-24 21:01:11 -07:00
parent a8917f095e
commit 2219035cd0
7 changed files with 107 additions and 47 deletions

View File

@@ -198,17 +198,25 @@ You should only enable Developer Mode if you meet one of the following requireme
- It is currently limited to a maximum depth of 3 to ensure it doesn't take too long to iterate over everything when you open it
- Very buggy
- There are multiple unimplemented actions";
"DevModeView.unstableFeaturesNightlyOnly" = "Unstable Features are only available on nightly builds or debug builds";
/* AsyncFallibleButton */
"AsyncFallibleButton.error" = "An error occurred";
/* AdvancedSettingsView */
"AdvancedSettingsView.title" = "Advanced Settings";
"AdvancedSettingsView.anisette" = "Anisette Server";
"AdvancedSettingsView.anisetteSettings" = "Anisette";
"AdvancedSettingsView.AnisetteSettings.server" = "Anisette Server";
"AdvancedSettingsView.AnisetteSettings.usePreferred" = "Use preferred servers";
"AdvancedSettingsView.AnisetteSettings.anisetteURL" = "Anisette URL";
"AdvancedSettingsView.AnisetteSettings.footer" = "If you disable \"Use preferred servers\" then SideStore will use the server you input into the \"Anisette URL\" box rather than one selected in \"Anisette Server\".";
"AdvancedSettingsView.dangerZone" = "Danger Zone";
"AdvancedSettingsView.dangerZoneInfo" = "If you disable \"Use preferred servers\" then SideStore will use the server you input into the \"Anisette URL\" box rather than one selected in \"Anisette Server\".";
"AdvancedSettingsView.DangerZone.usePreferred" = "Use preferred servers";
"AdvancedSettingsView.DangerZone.anisetteURL" = "Anisette URL";
/* UnstableFeaturesView */
"UnstableFeaturesView.title" = "Unstable Features";
"UnstableFeaturesView.description" = "Unstable Features are features that are currently being tested or still a work-in-progress and not ready for public usage. Because of this, they are only available on nightly builds. By default, all unstable features are off. Additionally, only more stable unstable features are available in Advanced Settings; most are locked behind Developer Mode to ensure normal users don't use them.
Every unstable feature has a tracking issue, which contains info on what the unstable feature adds and tracks the unstable feature status. To view a tracking issue for an unstable feature, simply click it in the list. **Please use the tracking issue for reporting bugs or giving feedback.**
**Do not ask for support on using unstable features, you will not receive any help.**";
"UnstableFeaturesView.noUnstableFeatures" = "There are currently no unstable features available.";

View File

@@ -37,19 +37,21 @@ internal enum L10n {
internal static let title = L10n.tr("Localizable", "AddSourceView.title", fallback: "Add Source")
}
internal enum AdvancedSettingsView {
/// Anisette Server
internal static let anisette = L10n.tr("Localizable", "AdvancedSettingsView.anisette", fallback: "Anisette Server")
/// Anisette
internal static let anisetteSettings = L10n.tr("Localizable", "AdvancedSettingsView.anisetteSettings", fallback: "Anisette")
/// Danger Zone
internal static let dangerZone = L10n.tr("Localizable", "AdvancedSettingsView.dangerZone", fallback: "Danger Zone")
/// If you disable "Use preferred servers" then SideStore will use the server you input into the "Anisette URL" box rather than one selected in "Anisette Server".
internal static let dangerZoneInfo = L10n.tr("Localizable", "AdvancedSettingsView.dangerZoneInfo", fallback: "If you disable \"Use preferred servers\" then SideStore will use the server you input into the \"Anisette URL\" box rather than one selected in \"Anisette Server\".")
/// AdvancedSettingsView
internal static let title = L10n.tr("Localizable", "AdvancedSettingsView.title", fallback: "Advanced Settings")
internal enum DangerZone {
internal enum AnisetteSettings {
/// Anisette URL
internal static let anisetteURL = L10n.tr("Localizable", "AdvancedSettingsView.DangerZone.anisetteURL", fallback: "Anisette URL")
internal static let anisetteURL = L10n.tr("Localizable", "AdvancedSettingsView.AnisetteSettings.anisetteURL", fallback: "Anisette URL")
/// If you disable "Use preferred servers" then SideStore will use the server you input into the "Anisette URL" box rather than one selected in "Anisette Server".
internal static let footer = L10n.tr("Localizable", "AdvancedSettingsView.AnisetteSettings.footer", fallback: "If you disable \"Use preferred servers\" then SideStore will use the server you input into the \"Anisette URL\" box rather than one selected in \"Anisette Server\".")
/// Anisette Server
internal static let server = L10n.tr("Localizable", "AdvancedSettingsView.AnisetteSettings.server", fallback: "Anisette Server")
/// Use preferred servers
internal static let usePreferred = L10n.tr("Localizable", "AdvancedSettingsView.DangerZone.usePreferred", fallback: "Use preferred servers")
internal static let usePreferred = L10n.tr("Localizable", "AdvancedSettingsView.AnisetteSettings.usePreferred", fallback: "Use preferred servers")
}
}
internal enum AppAction {
@@ -281,6 +283,8 @@ internal enum L10n {
internal static let title = L10n.tr("Localizable", "DevModeView.title", fallback: "Developer Mode")
/// Temporary File Explorer
internal static let tmpExplorer = L10n.tr("Localizable", "DevModeView.tmpExplorer", fallback: "Temporary File Explorer")
/// Unstable Features are only available on nightly builds or debug builds
internal static let unstableFeaturesNightlyOnly = L10n.tr("Localizable", "DevModeView.unstableFeaturesNightlyOnly", fallback: "Unstable Features are only available on nightly builds or debug builds")
internal enum Minimuxer {
/// AFC File Explorer (check footer for notes)
internal static let afcExplorer = L10n.tr("Localizable", "DevModeView.Minimuxer.afcExplorer", fallback: "AFC File Explorer (check footer for notes)")
@@ -406,6 +410,14 @@ internal enum L10n {
internal static let trustedSources = L10n.tr("Localizable", "SourcesView.trustedSources", fallback: "Trusted Sources")
}
internal enum UnstableFeaturesView {
/// Unstable Features are features that are currently being tested or still a work-in-progress and not ready for public usage. Because of this, they are only available on nightly builds. By default, all unstable features are off. Additionally, only more stable unstable features are available in Advanced Settings; most are locked behind Developer Mode to ensure normal users don't use them.
///
/// Every unstable feature has a tracking issue, which contains info on what the unstable feature adds and tracks the unstable feature status. To view a tracking issue for an unstable feature, simply click it in the list. **Please use the tracking issue for reporting bugs or giving feedback.**
///
/// **Do not ask for support on using unstable features, you will not receive any help.**
internal static let description = L10n.tr("Localizable", "UnstableFeaturesView.description", fallback: "Unstable Features are features that are currently being tested or still a work-in-progress and not ready for public usage. Because of this, they are only available on nightly builds. By default, all unstable features are off. Additionally, only more stable unstable features are available in Advanced Settings; most are locked behind Developer Mode to ensure normal users don't use them.\n\nEvery unstable feature has a tracking issue, which contains info on what the unstable feature adds and tracks the unstable feature status. To view a tracking issue for an unstable feature, simply click it in the list. **Please use the tracking issue for reporting bugs or giving feedback.**\n\n**Do not ask for support on using unstable features, you will not receive any help.**")
/// There are currently no unstable features available.
internal static let noUnstableFeatures = L10n.tr("Localizable", "UnstableFeaturesView.noUnstableFeatures", fallback: "There are currently no unstable features available.")
/// UnstableFeaturesView
internal static let title = L10n.tr("Localizable", "UnstableFeaturesView.title", fallback: "Unstable Features")
}

View File

@@ -40,27 +40,38 @@ struct AdvancedSettingsView: View {
var body: some View {
List {
Section {
Picker(L10n.AdvancedSettingsView.anisette, selection: $selectedAnisetteServer) {
Picker(L10n.AdvancedSettingsView.AnisetteSettings.server, selection: $selectedAnisetteServer) {
ForEach(anisetteServers) { server in
Text(server.display)
}
}
}
Section {
Toggle(L10n.AdvancedSettingsView.DangerZone.usePreferred, isOn: $usePreferred)
Toggle(L10n.AdvancedSettingsView.AnisetteSettings.usePreferred, isOn: $usePreferred)
HStack {
Text(L10n.AdvancedSettingsView.DangerZone.anisetteURL)
Text(L10n.AdvancedSettingsView.AnisetteSettings.anisetteURL)
TextField("", text: $anisetteURL)
.autocapitalization(.none)
.autocorrectionDisabled(true)
}
} header: {
Text(L10n.AdvancedSettingsView.dangerZone)
Text(L10n.AdvancedSettingsView.anisetteSettings)
} footer: {
Text(L10n.AdvancedSettingsView.dangerZoneInfo)
Text(L10n.AdvancedSettingsView.AnisetteSettings.footer)
}
#if UNSTABLE // TODO: remove this once we have more settings for the danger zone.
Section {
#if UNSTABLE
NavigationLink(L10n.UnstableFeaturesView.title) {
UnstableFeaturesView(inDevMode: false)
}
.foregroundColor(.red)
#endif
} header: {
Text(L10n.AdvancedSettingsView.dangerZone)
}
#endif
}
.navigationTitle(L10n.AdvancedSettingsView.title)
.enableInjection()

View File

@@ -128,6 +128,10 @@ struct DevModeMenu: View {
@AppStorage("isConsoleEnabled")
var isConsoleEnabled: Bool = false
#if !UNSTABLE
@State var isUnstableAlertShowing = false
#endif
var body: some View {
List {
Section {
@@ -143,6 +147,10 @@ struct DevModeMenu: View {
}
#if !UNSTABLE
.disabled(true)
.alert(isPresented: $isUnstableAlertShowing) {
Alert(title: Text(L10n.DevModeView.unstableFeaturesNightlyOnly))
}
.onTapGesture { isUnstableAlertShowing = true }
#endif
NavigationLink(L10n.DevModeView.dataExplorer) {

View File

@@ -104,6 +104,10 @@ struct SettingsView: View {
}
Section {
NavigationLink("Show Refresh Attempts") {
RefreshAttemptsView()
}
Toggle(isOn: self.$isBackgroundRefreshEnabled, label: {
Text(L10n.SettingsView.backgroundRefresh)
})
@@ -113,10 +117,6 @@ struct SettingsView: View {
SiriShortcutSetupView(shortcut: shortcut)
}
}
NavigationLink("Show Refresh Attempts") {
RefreshAttemptsView()
}
} header: {
Text(L10n.SettingsView.refreshingApps)
} footer: {
@@ -171,12 +171,6 @@ struct SettingsView: View {
AdvancedSettingsView()
}
#if UNSTABLE
NavigationLink(L10n.UnstableFeaturesView.title) {
UnstableFeaturesView(inDevMode: false)
}
#endif
Toggle(L10n.SettingsView.debugLogging, isOn: self.$isDebugLoggingEnabled)
.onChange(of: self.isDebugLoggingEnabled) { value in
UserDefaults.shared.isDebugLoggingEnabled = value

View File

@@ -10,20 +10,36 @@
import SwiftUI
struct UnstableFeaturesView: View {
@ObservedObject private var shared = UnstableFeatures.shared
@ObservedObject private var iO = Inject.observer
var inDevMode: Bool
var body: some View {
List {
ForEach(shared.features.filter { feature, _ in feature != .dummy && (inDevMode || feature.availableOutsideDevMode()) }.sorted(by: { _, _ in true }), id: \.key) { feature, _ in
Toggle(isOn: Binding(get: { UnstableFeatures.enabled(feature) }, set: { newValue in UnstableFeatures.set(feature, enabled: newValue) })) {
Text(String(describing: feature))
let link = "https://github.com/SideStore/SideStore/issues/\(feature.rawValue)"
Link(link, destination: URL(string: link)!)
let features = UnstableFeatures.getFeatures(inDevMode)
let description = L10n.UnstableFeaturesView.description + (features.count <= 0 ? "\n\n" + L10n.UnstableFeaturesView.noUnstableFeatures : "")
Section {} footer: {
if #available(iOS 15.0, *),
let string = try? AttributedString(markdown: description, options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)) {
Text(string).font(.callout).foregroundColor(.primary)
} else {
Text(description).font(.callout).foregroundColor(.primary)
}
}.listRowInsets(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
if features.count > 0 {
ForEach(features.sorted(by: { _, _ in true }), id: \.key) { feature, _ in
Toggle(isOn: Binding(get: { UnstableFeatures.enabled(feature) }, set: { newValue in UnstableFeatures.set(feature, enabled: newValue) })) {
Text(String(describing: feature))
let link = "https://github.com/SideStore/SideStore/issues/\(feature.rawValue)"
Link(link, destination: URL(string: link)!)
}
}
}
}.navigationTitle(L10n.UnstableFeaturesView.title)
}
.navigationTitle(L10n.UnstableFeaturesView.title)
.enableInjection()
}
}

View File

@@ -22,7 +22,7 @@ enum AvailableUnstableFeature: String, CaseIterable {
func availableOutsideDevMode() -> Bool {
switch self {
// If your unstable feature is stable enough to be used by nightly users who are not alpha testers or developers,
// you may want to have it available in the "Unstable Features" menu in Settings (outside of dev mode). To do so, add this:
// you may want to have it available in the Unstable Features menu in Advanced Settings (outside of dev mode). To do so, add this:
//case .yourFeature: return true
case .jitUrlScheme: return true
@@ -34,31 +34,42 @@ enum AvailableUnstableFeature: String, CaseIterable {
class UnstableFeatures: ObservableObject {
#if UNSTABLE
static let shared = UnstableFeatures()
@Published var features: [AvailableUnstableFeature: Bool] = [:]
private static var features: [AvailableUnstableFeature: Bool] = [:]
static func getFeatures(_ inDevMode: Bool) -> [(key: AvailableUnstableFeature, value: Bool)] {
return features
.filter { feature, _ in
feature != .dummy &&
(inDevMode || feature.availableOutsideDevMode())
}.sorted { a, b in a.key.rawValue > b.key.rawValue } // Convert to array of keys and values
}
static func load() {
if shared.features.count > 0 { return print("It seems unstable features have already been loaded, skipping") }
if features.count > 0 { return print("It seems unstable features have already been loaded, skipping") }
if let rawFeatures = UserDefaults.shared.unstableFeatures,
let rawFeatures = try? JSONDecoder().decode([String: Bool].self, from: rawFeatures) {
for rawFeature in rawFeatures {
if let feature = AvailableUnstableFeature.allCases.first(where: { feature in String(describing: feature) == rawFeature.key }) {
shared.features[feature] = rawFeature.value
features[feature] = rawFeature.value
} else {
print("Unknown unstable feature: \(rawFeature.key) = \(rawFeature.value)")
}
}
// If there's a new feature that wasn't saved and therefore wasn't loaded, let's set it to false
// Technically we shouldn't have to do this because enabled() will fallback to false
for feature in AvailableUnstableFeature.allCases {
if shared.features[feature] == nil {
shared.features[feature] = false
if features[feature] == nil {
features[feature] = false
}
}
save(load: true)
} else {
print("Setting all unstable features to false since we couldn't load them from UserDefaults (either they were never saved or there was an error decoding JSON)")
for feature in AvailableUnstableFeature.allCases {
shared.features[feature] = false
features[feature] = false
}
save()
}
@@ -66,7 +77,7 @@ class UnstableFeatures: ObservableObject {
private static func save(load: Bool = false) {
var rawFeatures: [String: Bool] = [:]
for feature in shared.features {
for feature in features {
rawFeatures[String(describing: feature.key)] = feature.value
}
UserDefaults.shared.unstableFeatures = try! JSONEncoder().encode(rawFeatures)
@@ -74,7 +85,7 @@ class UnstableFeatures: ObservableObject {
}
static func set(_ feature: AvailableUnstableFeature, enabled: Bool) {
shared.features[feature] = enabled
features[feature] = enabled
save()
}
#endif
@@ -82,7 +93,7 @@ class UnstableFeatures: ObservableObject {
@inline(__always) // hopefully this will help the compiler realize that if statements that use this function should be removed on non-unstable builds
static func enabled(_ feature: AvailableUnstableFeature) -> Bool {
#if UNSTABLE
shared.features[feature] ?? false
features[feature] ?? false
#else
false
#endif