Skip to content
Open
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
2 changes: 2 additions & 0 deletions MC1/MC1App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ struct MC1App: App {
.environment(\.appTheme, appState.themeService.current)
.tint(appState.themeService.current.chromeTint)
.preferredColorScheme(appState.themeService.effectiveColorScheme)
.uiScale(appState.themeService.uiTextSizePreference.uiScale)
.environment(\.dynamicTypeSize, appState.themeService.uiTextSizePreference.dynamicTypeSize ?? .large)
Comment on lines 93 to +95
#if !SIDELOAD
.task(id: ObjectIdentifier(appState)) { await appState.storeState.service.load() }
#endif
Expand Down
12 changes: 12 additions & 0 deletions MC1/Resources/Generated/L10n.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,18 @@ public enum L10n {
/// System
public static let system = L10n.tr("Settings", "Appearance.Scheme.System", fallback: "System")
}
public enum TextSize {
/// Text Size
public static let header = L10n.tr("Settings", "Appearance.TextSize.Header", fallback: "Text Size")
/// Large
public static let large = L10n.tr("Settings", "Appearance.TextSize.Large", fallback: "Large")
/// Default
public static let system = L10n.tr("Settings", "Appearance.TextSize.System", fallback: "Default")
/// Extra Large
public static let xLarge = L10n.tr("Settings", "Appearance.TextSize.XLarge", fallback: "Extra Large")
/// Extra Extra Large
public static let xxLarge = L10n.tr("Settings", "Appearance.TextSize.XXLarge", fallback: "Extra Extra Large")
}
public enum Themes {
/// Choose a theme
public static let header = L10n.tr("Settings", "Appearance.Themes.Header", fallback: "Choose a theme")
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/de.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "System";
"Appearance.Scheme.Light" = "Hell";
"Appearance.Scheme.Dark" = "Dunkel";
"Appearance.TextSize.Header" = "Textgröße";
"Appearance.TextSize.System" = "Standard";
"Appearance.TextSize.Large" = "Groß";
"Appearance.TextSize.XLarge" = "Sehr groß";
"Appearance.TextSize.XXLarge" = "Extra groß";
"Appearance.Themes.Header" = "Design auswählen";
"Appearance.Themes.Selected" = "Ausgewählt";
"Appearance.MoreThemes.Link" = "Weitere Designs kaufen";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/en.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,11 @@
"Appearance.Scheme.System" = "System";
"Appearance.Scheme.Light" = "Light";
"Appearance.Scheme.Dark" = "Dark";
"Appearance.TextSize.Header" = "Text Size";
"Appearance.TextSize.System" = "Default";
"Appearance.TextSize.Large" = "Large";
"Appearance.TextSize.XLarge" = "Extra Large";
"Appearance.TextSize.XXLarge" = "Extra Extra Large";
"Appearance.Themes.Header" = "Choose a theme";
"Appearance.Themes.Selected" = "Selected";
"Appearance.MoreThemes.Link" = "Purchase More Themes";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/es.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Sistema";
"Appearance.Scheme.Light" = "Claro";
"Appearance.Scheme.Dark" = "Oscuro";
"Appearance.TextSize.Header" = "Tamaño de texto";
"Appearance.TextSize.System" = "Predeterminado";
"Appearance.TextSize.Large" = "Grande";
"Appearance.TextSize.XLarge" = "Muy grande";
"Appearance.TextSize.XXLarge" = "Extra grande";
"Appearance.Themes.Header" = "Elige un tema";
"Appearance.Themes.Selected" = "Seleccionado";
"Appearance.MoreThemes.Link" = "Comprar más temas";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/fr.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Système";
"Appearance.Scheme.Light" = "Clair";
"Appearance.Scheme.Dark" = "Sombre";
"Appearance.TextSize.Header" = "Taille du texte";
"Appearance.TextSize.System" = "Par défaut";
"Appearance.TextSize.Large" = "Grande";
"Appearance.TextSize.XLarge" = "Très grande";
"Appearance.TextSize.XXLarge" = "Extra grande";
"Appearance.Themes.Header" = "Choisir un thème";
"Appearance.Themes.Selected" = "Sélectionné";
"Appearance.MoreThemes.Link" = "Acheter d'autres thèmes";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/it.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,11 @@
"Appearance.Scheme.System" = "Sistema";
"Appearance.Scheme.Light" = "Chiara";
"Appearance.Scheme.Dark" = "Scura";
"Appearance.TextSize.Header" = "Dimensione testo";
"Appearance.TextSize.System" = "Predefinita";
"Appearance.TextSize.Large" = "Grande";
"Appearance.TextSize.XLarge" = "Molto grande";
"Appearance.TextSize.XXLarge" = "Extra grande";
"Appearance.Themes.Header" = "Scegli un tema";
"Appearance.Themes.Selected" = "Selezionato";
"Appearance.MoreThemes.Link" = "Acquista altri temi";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/nl.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Systeem";
"Appearance.Scheme.Light" = "Licht";
"Appearance.Scheme.Dark" = "Donker";
"Appearance.TextSize.Header" = "Tekengrootte";
"Appearance.TextSize.System" = "Standaard";
"Appearance.TextSize.Large" = "Groot";
"Appearance.TextSize.XLarge" = "Extra groot";
"Appearance.TextSize.XXLarge" = "Extra extra groot";
"Appearance.Themes.Header" = "Kies een thema";
"Appearance.Themes.Selected" = "Geselecteerd";
"Appearance.MoreThemes.Link" = "Meer thema's kopen";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/pl.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Systemowy";
"Appearance.Scheme.Light" = "Jasny";
"Appearance.Scheme.Dark" = "Ciemny";
"Appearance.TextSize.Header" = "Rozmiar tekstu";
"Appearance.TextSize.System" = "Domyślny";
"Appearance.TextSize.Large" = "Duży";
"Appearance.TextSize.XLarge" = "Bardzo duży";
"Appearance.TextSize.XXLarge" = "Ekstra duży";
"Appearance.Themes.Header" = "Wybierz motyw";
"Appearance.Themes.Selected" = "Wybrany";
"Appearance.MoreThemes.Link" = "Kup więcej motywów";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/ru.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Системная";
"Appearance.Scheme.Light" = "Светлая";
"Appearance.Scheme.Dark" = "Тёмная";
"Appearance.TextSize.Header" = "Размер текста";
"Appearance.TextSize.System" = "По умолчанию";
"Appearance.TextSize.Large" = "Крупный";
"Appearance.TextSize.XLarge" = "Очень крупный";
"Appearance.TextSize.XXLarge" = "Экстра крупный";
"Appearance.Themes.Header" = "Выберите тему";
"Appearance.Themes.Selected" = "Выбрана";
"Appearance.MoreThemes.Link" = "Купить другие темы";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/uk.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "Системна";
"Appearance.Scheme.Light" = "Світла";
"Appearance.Scheme.Dark" = "Темна";
"Appearance.TextSize.Header" = "Розмір тексту";
"Appearance.TextSize.System" = "За замовчуванням";
"Appearance.TextSize.Large" = "Великий";
"Appearance.TextSize.XLarge" = "Дуже великий";
"Appearance.TextSize.XXLarge" = "Екстра великий";
"Appearance.Themes.Header" = "Виберіть тему";
"Appearance.Themes.Selected" = "Вибрано";
"Appearance.MoreThemes.Link" = "Придбати інші теми";
Expand Down
5 changes: 5 additions & 0 deletions MC1/Resources/Localization/zh-Hans.lproj/Settings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,11 @@
"Appearance.Scheme.System" = "跟随系统";
"Appearance.Scheme.Light" = "浅色";
"Appearance.Scheme.Dark" = "深色";
"Appearance.TextSize.Header" = "文本大小";
"Appearance.TextSize.System" = "默认";
"Appearance.TextSize.Large" = "大";
"Appearance.TextSize.XLarge" = "特大";
"Appearance.TextSize.XXLarge" = "超大";
"Appearance.Themes.Header" = "选择主题";
"Appearance.Themes.Selected" = "已选择";
"Appearance.MoreThemes.Link" = "购买更多主题";
Expand Down
38 changes: 38 additions & 0 deletions MC1/Theme/AppUITextSizePreference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import SwiftUI

/// Global app text-size preference, independent of which theme is selected.
/// Raw values are pinned (persisted + backed up); a rename must not change the on-disk format.
enum AppUITextSizePreference: String, CaseIterable, Identifiable {
case system
case large
case xLarge
case xxLarge

var id: String {
rawValue
}

/// `nil` means "defer to the system" — the value passed to `.dynamicTypeSize(_:)`.
/// `.system` maps to `nil` so the app keeps its current appearance: the root
/// override falls back to the `.large` preset, which `dynamicTypeSize(_:)`
/// treats as "no override" (no visual change for existing users).
Comment on lines +15 to +18
var dynamicTypeSize: DynamicTypeSize? {
switch self {
case .system: nil
case .large: .xLarge
case .xLarge: .xxLarge
case .xxLarge: .accessibility1
}
}

/// Render-scale factor applied on Mac ("Designed for iPad"), where Dynamic Type
/// is disabled by the platform and no text/trait override works.
var uiScale: CGFloat {
switch self {
case .system: 1.0
case .large: 1.2
case .xLarge: 1.35
case .xxLarge: 1.5
}
}
}
32 changes: 32 additions & 0 deletions MC1/Theme/ThemeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI
final class ThemeService {
private(set) var current: Theme
private(set) var colorSchemePreference: AppColorSchemePreference
private(set) var uiTextSizePreference: AppUITextSizePreference
private let store: StoreService
private let defaults: UserDefaults

Expand Down Expand Up @@ -42,6 +43,17 @@ final class ThemeService {
forKey: PersistenceKeys.appColorSchemePreference)
}

let storedTextSize = defaults.string(forKey: PersistenceKeys.appUITextSizePreference)
if let raw = storedTextSize, let preference = AppUITextSizePreference(rawValue: raw) {
uiTextSizePreference = preference
} else if storedTextSize == nil {
uiTextSizePreference = .system // missing key: no write-back
} else {
uiTextSizePreference = .system // unknown raw value: fall back and overwrite
defaults.set(AppUITextSizePreference.system.rawValue,
forKey: PersistenceKeys.appUITextSizePreference)
}

// StoreService is constructed before ThemeService, so this callback is wired before
// any Transaction.updates emission.
store.onEntitlementsChanged = { [weak self] in
Expand All @@ -62,6 +74,11 @@ final class ThemeService {
defaults.set(preference.rawValue, forKey: PersistenceKeys.appColorSchemePreference)
}

func setUITextSizePreference(_ preference: AppUITextSizePreference) {
uiTextSizePreference = preference
defaults.set(preference.rawValue, forKey: PersistenceKeys.appUITextSizePreference)
}

/// Theme-forced override wins; otherwise the user's global preference applies.
var effectiveColorScheme: ColorScheme? {
current.preferredColorScheme ?? colorSchemePreference.colorScheme
Expand Down Expand Up @@ -89,6 +106,9 @@ final class ThemeService {

let resolvedScheme = resolveSchemePreferenceFromDefaults()
if resolvedScheme != colorSchemePreference { colorSchemePreference = resolvedScheme }

let resolvedTextSize = resolveTextSizePreferenceFromDefaults()
if resolvedTextSize != uiTextSizePreference { uiTextSizePreference = resolvedTextSize }
}

private func resolveThemeFromDefaults() -> Theme {
Expand Down Expand Up @@ -123,6 +143,18 @@ final class ThemeService {
return .system
}

private func resolveTextSizePreferenceFromDefaults() -> AppUITextSizePreference {
let storedTextSize = defaults.string(forKey: PersistenceKeys.appUITextSizePreference)
if let raw = storedTextSize, let preference = AppUITextSizePreference(rawValue: raw) {
return preference
}
if storedTextSize == nil { return .system } // missing key: no write-back
// Unknown raw value (e.g., a future-build case via backup): fall back + corrective overwrite.
defaults.set(AppUITextSizePreference.system.rawValue,
forKey: PersistenceKeys.appUITextSizePreference)
return .system
}

/// Idempotent: invoked from `StoreService.onEntitlementsChanged`. If the current theme is no
/// longer accessible (e.g. a refund), revert to default, persist, and announce for VoiceOver.
///
Expand Down
41 changes: 39 additions & 2 deletions MC1/Views/Appearance/AppearanceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ struct AppearanceView: View {

@State private var schemeTrigger = 0
@State private var selectionTrigger = 0
@State private var textSizeTrigger = 0

@ScaledMetric(relativeTo: .body) private var gridItemMinimum = ThemeCardMetrics.gridItemMinimum
@ScaledMetric(relativeTo: .body) private var gridSpacing = ThemeCardMetrics.gridSpacing

private var themeService: ThemeService {
appState.themeService
Expand All @@ -20,7 +24,7 @@ struct AppearanceView: View {
private var columns: [GridItem] {
dynamicTypeSize.isAccessibilitySize
? [GridItem(.flexible())]
: [GridItem(.adaptive(minimum: ThemeCardMetrics.gridItemMinimum), spacing: ThemeCardMetrics.gridSpacing)]
: [GridItem(.adaptive(minimum: gridItemMinimum), spacing: gridSpacing)]
}

/// "Purchase More Themes" is shown only when at least one registry theme is unavailable.
Expand All @@ -30,6 +34,7 @@ struct AppearanceView: View {

var body: some View {
List {
textSizeSection
schemeSection
themesSection
if Self.shouldShowBrowseMore(available: availableThemes) {
Expand All @@ -46,6 +51,19 @@ struct AppearanceView: View {
.navigationBarTitleDisplayMode(.inline)
.sensoryFeedback(.selection, trigger: schemeTrigger)
.sensoryFeedback(.selection, trigger: selectionTrigger)
.sensoryFeedback(.selection, trigger: textSizeTrigger)
}

private var textSizeSection: some View {
Section {
Picker(L10n.Settings.Appearance.TextSize.header, selection: textSizeBinding) {
ForEach(AppUITextSizePreference.allCases) { preference in
Text(textSizeLabel(preference)).tag(preference)
}
}
.pickerStyle(.menu)
}
.themedRowBackground(activeTheme)
}

private var schemeSection: some View {
Expand All @@ -62,7 +80,7 @@ struct AppearanceView: View {

private var themesSection: some View {
Section {
LazyVGrid(columns: columns, spacing: ThemeCardMetrics.gridSpacing) {
LazyVGrid(columns: columns, spacing: gridSpacing) {
ForEach(availableThemes) { theme in
ThemeSelectionCard(
theme: theme,
Expand All @@ -88,6 +106,16 @@ struct AppearanceView: View {
)
}

private var textSizeBinding: Binding<AppUITextSizePreference> {
Binding(
get: { themeService.uiTextSizePreference },
set: {
themeService.setUITextSizePreference($0)
textSizeTrigger += 1
}
)
}

private func schemeLabel(_ preference: AppColorSchemePreference) -> String {
switch preference {
case .system: L10n.Settings.Appearance.Scheme.system
Expand All @@ -96,6 +124,15 @@ struct AppearanceView: View {
}
}

private func textSizeLabel(_ preference: AppUITextSizePreference) -> String {
switch preference {
case .system: L10n.Settings.Appearance.TextSize.system
case .large: L10n.Settings.Appearance.TextSize.large
case .xLarge: L10n.Settings.Appearance.TextSize.xLarge
case .xxLarge: L10n.Settings.Appearance.TextSize.xxLarge
}
}

private func select(_ theme: Theme) {
// The list is already filtered to accessible themes; the throw path is a defensive
// guard against a refund-driven revert landing mid-tap.
Expand Down
13 changes: 9 additions & 4 deletions MC1/Views/Appearance/Components/ThemeSelectionCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ struct ThemeSelectionCard: View {
let isSelected: Bool
let onSelect: () -> Void

@ScaledMetric(relativeTo: .body) private var badgeIconSpacing = ThemeCardMetrics.badgeIconSpacing
@ScaledMetric(relativeTo: .body) private var contentPadding = ThemeCardMetrics.contentPadding
@ScaledMetric(relativeTo: .body) private var cornerRadius = ThemeCardMetrics.cornerRadius
@ScaledMetric(relativeTo: .body) private var selectionStrokeWidth = ThemeCardMetrics.selectionStrokeWidth

var body: some View {
Button(action: onSelect) {
VStack(spacing: 8) {
Expand All @@ -14,7 +19,7 @@ struct ThemeSelectionCard: View {
.font(.subheadline.weight(.medium))
.lineLimit(1)
if isSelected {
HStack(spacing: ThemeCardMetrics.badgeIconSpacing) {
HStack(spacing: badgeIconSpacing) {
Image(systemName: "checkmark")
Text(L10n.Settings.Appearance.Themes.selected)
}
Expand All @@ -24,11 +29,11 @@ struct ThemeSelectionCard: View {
Color.clear.frame(height: 1)
}
}
.padding(ThemeCardMetrics.contentPadding)
.padding(contentPadding)
.frame(maxWidth: .infinity)
.overlay(
RoundedRectangle(cornerRadius: ThemeCardMetrics.cornerRadius)
.strokeBorder(isSelected ? Color.accentColor : .clear, lineWidth: ThemeCardMetrics.selectionStrokeWidth)
RoundedRectangle(cornerRadius: cornerRadius)
.strokeBorder(isSelected ? Color.accentColor : .clear, lineWidth: selectionStrokeWidth)
)
}
.buttonStyle(.plain)
Expand Down
Loading