feat: Add release notes system with auto-popup on version updates

Implement comprehensive release notes feature:
- RELEASE_NOTES.md with version 1.0 and 1.1 content in English
- VersionManager to track app versions and detect updates
- ReleaseNotesView with native markdown rendering
- Auto-popup sheet on first launch after version update
- Manual access via "What's New" button in General Settings

Features:
- Markdown-based release notes stored in app bundle
- Automatic version detection using CFBundleShortVersionString
- UserDefaults tracking of last seen version
- Dismissable sheet with "Done" button
- Settings button shows current version number

Technical implementation:
- VersionManager singleton for version tracking
- Sheet presentation in MainTabView on new version
- Settings integration with sparkles icon
- Native SwiftUI Text markdown rendering
- Bundle resource loading for RELEASE_NOTES.md

Release notes content:
- Version 1.1: iOS 26 features, floating buttons, progress tracking
- Version 1.0: Initial release features and capabilities
This commit is contained in:
Ilyas Hallak 2025-10-14 14:04:28 +02:00
parent e61dbc7d72
commit b8e5766cb1
5 changed files with 199 additions and 11 deletions

View File

@ -0,0 +1,68 @@
# Release Notes
## Version 1.1 (Build 1)
### iOS 26+ Native WebView
- **New native SwiftUI WebView implementation** for iOS 26 and later
- Improved performance with native WebKit integration
- Better memory management and rendering
### Floating Action Buttons
- **Contextual action buttons** appear when reaching 90% of article
- Beautiful glass effect design with liquid interactions
- Smooth slide-up animation
- Quick access to favorite and archive actions
### Reading Progress Improvements
- **Accurate progress tracking** using optimized PreferenceKey approach
- Progress bar reflects entire article length (header, content, metadata)
- Automatic progress sync every 3% to reduce API calls
- Progress locked at 100% to prevent fluctuations
### Image Header Enhancement
- **Better image display** with aspect fit and blurred background
- No more random cropping - full image visibility
- Maintains header space while showing complete images
### Performance Optimizations
- Replaced onScrollGeometryChange with PreferenceKey for smoother scrolling
- Reduced state updates during scroll
- Optimized WebView height detection
- Improved CSS rendering for web content
### Bug Fixes
- Fixed content width overflow in native WebView
- Fixed excessive spacing between header and content
- Fixed read progress calculation to include all content sections
- Fixed JavaScript height detection with simplified approach
---
## Version 1.0 (Initial Release)
### Core Features
- Browse and read saved articles
- Bookmark management with labels
- Full article view with custom fonts
- Text-to-speech support (Beta)
- Archive and favorite functionality
### Reading Experience
- Clean, distraction-free reading interface
- Customizable font settings
- Image viewer with zoom support
- Progress tracking per article
- Dark mode support
### Organization
- Label system for categorization
- Search and filter bookmarks
- Archive completed articles
- Jump to last read position
### Share Extension
- Save articles from other apps
- Quick access to save and label bookmarks
- Save Bookmarks offline if your server is not reachable and sync later

View File

@ -5,21 +5,37 @@ struct MainTabView: View {
@State private var selectedTab: SidebarTab = .unread
@State var selectedBookmark: Bookmark?
@StateObject private var playerUIState = PlayerUIState()
@State private var showReleaseNotes = false
// sizeClass
@Environment(\.horizontalSizeClass)
var horizontalSizeClass
@Environment(\.verticalSizeClass)
var verticalSizeClass
var body: some View {
if UIDevice.isPhone {
PhoneTabView()
.environmentObject(playerUIState)
} else {
PadSidebarView()
.environmentObject(playerUIState)
Group {
if UIDevice.isPhone {
PhoneTabView()
.environmentObject(playerUIState)
} else {
PadSidebarView()
.environmentObject(playerUIState)
}
}
.sheet(isPresented: $showReleaseNotes) {
ReleaseNotesView()
}
.onAppear {
checkForNewVersion()
}
}
private func checkForNewVersion() {
if VersionManager.shared.isNewVersion {
showReleaseNotes = true
VersionManager.shared.markVersionAsSeen()
}
}
}

View File

@ -0,0 +1,44 @@
import SwiftUI
struct ReleaseNotesView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
if let markdownContent = loadReleaseNotes() {
Text(.init(markdownContent))
.font(.body)
.padding()
} else {
Text("Unable to load release notes")
.foregroundColor(.secondary)
.padding()
}
}
}
.navigationTitle("What's New")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
dismiss()
}
}
}
}
}
private func loadReleaseNotes() -> String? {
guard let url = Bundle.main.url(forResource: "RELEASE_NOTES", withExtension: "md"),
let content = try? String(contentsOf: url) else {
return nil
}
return content
}
}
#Preview {
ReleaseNotesView()
}

View File

@ -9,11 +9,12 @@ import SwiftUI
struct SettingsGeneralView: View {
@State private var viewModel: SettingsGeneralViewModel
@State private var showReleaseNotes = false
init(viewModel: SettingsGeneralViewModel = SettingsGeneralViewModel()) {
self.viewModel = viewModel
}
var body: some View {
VStack(spacing: 20) {
SectionHeader(title: "General Settings".localized, icon: "gear")
@ -22,6 +23,24 @@ struct SettingsGeneralView: View {
VStack(alignment: .leading, spacing: 12) {
Text("General")
.font(.headline)
// What's New Button
Button(action: {
showReleaseNotes = true
}) {
HStack {
Label("What's New", systemImage: "sparkles")
Spacer()
Text("Version \(VersionManager.shared.currentVersion)")
.font(.caption)
.foregroundColor(.secondary)
Image(systemName: "chevron.right")
.font(.caption)
.foregroundColor(.secondary)
}
}
.buttonStyle(.plain)
Toggle("Read Aloud Feature", isOn: $viewModel.enableTTS)
.toggleStyle(.switch)
.onChange(of: viewModel.enableTTS) {
@ -98,6 +117,9 @@ struct SettingsGeneralView: View {
#endif
}
.sheet(isPresented: $showReleaseNotes) {
ReleaseNotesView()
}
.task {
await viewModel.loadGeneralSettings()
}

View File

@ -0,0 +1,38 @@
import Foundation
class VersionManager {
static let shared = VersionManager()
private let lastSeenVersionKey = "lastSeenAppVersion"
private let userDefaults = UserDefaults.standard
var currentVersion: String {
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0"
}
var currentBuild: String {
Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1"
}
var fullVersion: String {
"\(currentVersion) (\(currentBuild))"
}
var lastSeenVersion: String? {
userDefaults.string(forKey: lastSeenVersionKey)
}
var isNewVersion: Bool {
guard let lastSeen = lastSeenVersion else {
// First launch
return true
}
return lastSeen != currentVersion
}
func markVersionAsSeen() {
userDefaults.set(currentVersion, forKey: lastSeenVersionKey)
}
private init() {}
}