ReadKeep/readeck/UI/Settings/FontSelectionView.swift
Ilyas Hallak a227c275f3 feat: Implement extended font system and offline sync improvements
- Add 10 new fonts (Literata, Merriweather, Source Serif, Lato, Montserrat, Source Sans)
- Support Apple system fonts and Google Fonts (OFL 1.1 licensed)
- Extend FontFamily enum with new fonts and categories
- Update FontSettingsViewModel and WebView with font support
- Force WebView reload when font settings change
- Refactor OfflineSyncManager with protocol and improved error handling
- Add test mocks and OfflineSyncManagerTests with 9 test cases
- Add OpenSourceLicensesView and FontDebugView
- Bump build number
- Update localization strings
2025-12-10 21:25:39 +01:00

110 lines
3.8 KiB
Swift

//
// FontSelectionView.swift
// readeck
//
// Created by Ilyas Hallak on 08.11.25.
//
import SwiftUI
struct FontSelectionView: View {
@State private var viewModel: FontSettingsViewModel
@Environment(\.dismiss) private var dismiss
init(viewModel: FontSettingsViewModel = FontSettingsViewModel()) {
self.viewModel = viewModel
}
var body: some View {
List {
// Preview Section
Section {
VStack(alignment: .leading, spacing: 12) {
Text("readeck Bookmark Title")
.font(viewModel.previewTitleFont)
.fontWeight(.semibold)
.lineLimit(2)
Text("This is how your bookmark descriptions and article text will appear in the app. The quick brown fox jumps over the lazy dog. Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
.font(viewModel.previewBodyFont)
.lineLimit(4)
Text("12 min • Today • example.com")
.font(viewModel.previewCaptionFont)
.foregroundColor(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(color: Color.black.opacity(0.08), radius: 8, x: 0, y: 2)
.listRowInsets(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
.listRowBackground(Color.clear)
} header: {
Text("Preview")
.font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.textCase(nil)
}
// Font Settings Section
Section {
Picker("Font family", selection: $viewModel.selectedFontFamily) {
ForEach(FontFamily.allCases, id: \.self) { family in
Text(family.displayName).tag(family)
}
}
.onChange(of: viewModel.selectedFontFamily) {
Task {
await viewModel.saveFontSettings()
}
}
Text("font.web.match.hint".localized)
.font(.caption)
.foregroundColor(.secondary)
VStack(alignment: .leading, spacing: 8) {
Text("Font size")
.font(.subheadline)
.foregroundColor(.primary)
Picker("Font size", selection: $viewModel.selectedFontSize) {
ForEach(FontSize.allCases, id: \.self) { size in
Text(size.displayName).tag(size)
}
}
.pickerStyle(.segmented)
.onChange(of: viewModel.selectedFontSize) {
Task {
await viewModel.saveFontSettings()
}
}
}
.padding(.vertical, 4)
} header: {
Text("Font Settings")
.font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.textCase(nil)
}
}
.listStyle(.insetGrouped)
.navigationTitle("Font")
.navigationBarTitleDisplayMode(.inline)
.task {
await viewModel.loadFontSettings()
}
}
}
#Preview {
NavigationStack {
FontSelectionView(viewModel: .init(
factory: MockUseCaseFactory()
))
}
}