Compare commits
No commits in common. "eddc8a35ff82990b8fb5bde9f8d81937f4823ed3" and "f302f8800f92decf5da8477cca3920d09b3fda6b" have entirely different histories.
eddc8a35ff
...
f302f8800f
@ -437,7 +437,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = URLShare/URLShare.entitlements;
|
CODE_SIGN_ENTITLEMENTS = URLShare/URLShare.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 29;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_TEAM = 8J69P655GN;
|
DEVELOPMENT_TEAM = 8J69P655GN;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = URLShare/Info.plist;
|
INFOPLIST_FILE = URLShare/Info.plist;
|
||||||
@ -470,7 +470,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = URLShare/URLShare.entitlements;
|
CODE_SIGN_ENTITLEMENTS = URLShare/URLShare.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 29;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_TEAM = 8J69P655GN;
|
DEVELOPMENT_TEAM = 8J69P655GN;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = URLShare/Info.plist;
|
INFOPLIST_FILE = URLShare/Info.plist;
|
||||||
@ -625,7 +625,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = readeck/readeck.entitlements;
|
CODE_SIGN_ENTITLEMENTS = readeck/readeck.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 29;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"readeck/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"readeck/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 8J69P655GN;
|
DEVELOPMENT_TEAM = 8J69P655GN;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@ -669,7 +669,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = readeck/readeck.entitlements;
|
CODE_SIGN_ENTITLEMENTS = readeck/readeck.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 29;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"readeck/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"readeck/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 8J69P655GN;
|
DEVELOPMENT_TEAM = 8J69P655GN;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
|||||||
@ -1,87 +0,0 @@
|
|||||||
# Release Notes
|
|
||||||
|
|
||||||
Thanks for using the Readeck iOS app! Below are the release notes for each version.
|
|
||||||
|
|
||||||
**AppStore:** The App is now in the App Store! [Get it here](https://apps.apple.com/de/app/readeck/id6748764703) for all TestFlight users. If you wish a more stable Version, please download it from there. Or you can continue using TestFlight for the latest features.
|
|
||||||
|
|
||||||
## Version 1.1
|
|
||||||
|
|
||||||
There is a lot of feature reqeusts and improvements in this release which are based on your feedback. Thank you so much for that! If you like the new features, please consider leaving a review on the App Store to support further development.
|
|
||||||
|
|
||||||
### Modern Reading Experience (iOS 26+)
|
|
||||||
|
|
||||||
- **Completely rebuilt article view** for the latest iOS version
|
|
||||||
- Smoother scrolling and faster page loading
|
|
||||||
- Better battery life and memory usage
|
|
||||||
- Native iOS integration for the best experience
|
|
||||||
|
|
||||||
### Quick Actions
|
|
||||||
|
|
||||||
- **Smart action buttons** appear automatically when you're almost done reading
|
|
||||||
- Beautiful, modern design that blends with your content
|
|
||||||
- Quickly favorite or archive articles without scrolling back up
|
|
||||||
- Buttons fade away elegantly when you scroll back
|
|
||||||
- Your progress bar now reflects the entire article length
|
|
||||||
|
|
||||||
### Beautiful Article Images
|
|
||||||
|
|
||||||
- **Article header images now display properly** without awkward cropping
|
|
||||||
- Full images with a subtle blurred background
|
|
||||||
- Tap to view images in full screen
|
|
||||||
|
|
||||||
### Smoother Performance
|
|
||||||
|
|
||||||
- **Dramatically improved scrolling** - no more stuttering or lag
|
|
||||||
- Faster article loading times
|
|
||||||
- Better handling of long articles with many images
|
|
||||||
- Overall snappier app experience
|
|
||||||
|
|
||||||
### Open Links Your Way
|
|
||||||
|
|
||||||
- **Choose your preferred browser** for opening links
|
|
||||||
- Open in Safari or in-app browser
|
|
||||||
- Thanks to christian-putzke for this contribution!
|
|
||||||
|
|
||||||
### Fixes & Improvements
|
|
||||||
|
|
||||||
- Articles no longer overflow the screen width
|
|
||||||
- Fixed spacing issues in article view
|
|
||||||
- Improved progress calculation accuracy
|
|
||||||
- Better handling of article content
|
|
||||||
- Fixed issues with label names containing spaces
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
|
||||||
- Choose different Layouts (Compact, Magazine, Natural)
|
|
||||||
|
|
||||||
### Reading Experience
|
|
||||||
|
|
||||||
- Clean, distraction-free reading interface
|
|
||||||
- Customizable font settings
|
|
||||||
- Header Image viewer with zoom support
|
|
||||||
- Progress tracking per article
|
|
||||||
- Dark mode support
|
|
||||||
|
|
||||||
### Organization
|
|
||||||
|
|
||||||
- Label system for categorization (multi-select)
|
|
||||||
- Search
|
|
||||||
- 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
|
|
||||||
|
|
||||||
|
|
||||||
@ -197,7 +197,6 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
#if DEBUG
|
|
||||||
// Toggle button (left)
|
// Toggle button (left)
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
if #available(iOS 26.0, *) {
|
if #available(iOS 26.0, *) {
|
||||||
@ -209,7 +208,6 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Top toolbar (right)
|
// Top toolbar (right)
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
|
|||||||
@ -39,11 +39,20 @@ struct BookmarkDetailView2: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var mainView: some View {
|
private var mainView: some View {
|
||||||
content
|
VStack(spacing: 0) {
|
||||||
|
// Progress bar at top
|
||||||
|
ProgressView(value: readingProgress)
|
||||||
|
.progressViewStyle(LinearProgressViewStyle())
|
||||||
|
.frame(height: 3)
|
||||||
|
|
||||||
|
// Main scroll content
|
||||||
|
scrollViewContent
|
||||||
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
toolbarContent
|
toolbarContent
|
||||||
}
|
}
|
||||||
|
.toolbarBackgroundVisibility(.hidden, for: .bottomBar)
|
||||||
.sheet(isPresented: $showingFontSettings) {
|
.sheet(isPresented: $showingFontSettings) {
|
||||||
fontSettingsSheet
|
fontSettingsSheet
|
||||||
}
|
}
|
||||||
@ -76,61 +85,6 @@ struct BookmarkDetailView2: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var content: some View {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
// Progress bar at top
|
|
||||||
ProgressView(value: readingProgress)
|
|
||||||
.progressViewStyle(LinearProgressViewStyle())
|
|
||||||
.frame(height: 3)
|
|
||||||
|
|
||||||
// Main scroll content
|
|
||||||
scrollViewContent
|
|
||||||
.overlay(alignment: .bottomTrailing) {
|
|
||||||
if viewModel.isLoadingArticle == false && viewModel.isLoading == false {
|
|
||||||
if readingProgress >= 0.9 {
|
|
||||||
floatingActionButtons
|
|
||||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.spring(response: 0.6, dampingFraction: 0.8), value: readingProgress >= 0.9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var floatingActionButtons: some View {
|
|
||||||
GlassEffectContainer(spacing: 52.0) {
|
|
||||||
HStack(spacing: 52.0) {
|
|
||||||
Button(action: {
|
|
||||||
Task {
|
|
||||||
await viewModel.toggleFavorite(id: bookmarkId)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Image(systemName: viewModel.bookmarkDetail.isMarked ? "star.fill" : "star")
|
|
||||||
.foregroundStyle(viewModel.bookmarkDetail.isMarked ? .yellow : .primary)
|
|
||||||
.frame(width: 52.0, height: 52.0)
|
|
||||||
.font(.system(size: 31))
|
|
||||||
}
|
|
||||||
.disabled(viewModel.isLoading)
|
|
||||||
.glassEffect()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
Task {
|
|
||||||
await viewModel.archiveBookmark(id: bookmarkId, isArchive: !viewModel.bookmarkDetail.isArchived)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Image(systemName: viewModel.bookmarkDetail.isArchived ? "checkmark.circle" : "archivebox")
|
|
||||||
.frame(width: 52.0, height: 52.0)
|
|
||||||
.font(.system(size: 31))
|
|
||||||
}
|
|
||||||
.disabled(viewModel.isLoading)
|
|
||||||
.glassEffect()
|
|
||||||
.offset(x: -52.0, y: 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.trailing, 1)
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var scrollViewContent: some View {
|
private var scrollViewContent: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@ -182,7 +136,7 @@ struct BookmarkDetailView2: View {
|
|||||||
}
|
}
|
||||||
.coordinateSpace(name: "scrollView")
|
.coordinateSpace(name: "scrollView")
|
||||||
.clipped()
|
.clipped()
|
||||||
.ignoresSafeArea(edges: [.top, .bottom])
|
.ignoresSafeArea(edges: .top)
|
||||||
.scrollPosition($scrollPosition)
|
.scrollPosition($scrollPosition)
|
||||||
.onPreferenceChange(ContentHeightPreferenceKey.self) { endPosition in
|
.onPreferenceChange(ContentHeightPreferenceKey.self) { endPosition in
|
||||||
contentEndPosition = endPosition
|
contentEndPosition = endPosition
|
||||||
@ -217,10 +171,9 @@ struct BookmarkDetailView2: View {
|
|||||||
let reachedEnd = progress >= 1.0 && lastSentProgress < 1.0
|
let reachedEnd = progress >= 1.0 && lastSentProgress < 1.0
|
||||||
let shouldUpdate = abs(progress - lastSentProgress) >= threshold || reachedEnd
|
let shouldUpdate = abs(progress - lastSentProgress) >= threshold || reachedEnd
|
||||||
|
|
||||||
readingProgress = progress
|
|
||||||
|
|
||||||
if shouldUpdate {
|
if shouldUpdate {
|
||||||
lastSentProgress = progress
|
lastSentProgress = progress
|
||||||
|
readingProgress = progress
|
||||||
viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: progress, anchor: nil)
|
viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: progress, anchor: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,6 +214,40 @@ struct BookmarkDetailView2: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
// Bottom toolbar - Archive section
|
||||||
|
if viewModel.isLoadingArticle == false && viewModel.isLoading == false {
|
||||||
|
ToolbarItemGroup(placement: .bottomBar) {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
Task {
|
||||||
|
await viewModel.toggleFavorite(id: bookmarkId)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Label(
|
||||||
|
viewModel.bookmarkDetail.isMarked ? "Favorited" : "Favorite",
|
||||||
|
systemImage: viewModel.bookmarkDetail.isMarked ? "star.fill" : "star"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.disabled(viewModel.isLoading)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
Task {
|
||||||
|
await viewModel.archiveBookmark(id: bookmarkId, isArchive: !viewModel.bookmarkDetail.isArchived)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Label(
|
||||||
|
viewModel.bookmarkDetail.isArchived ? "Unarchive" : "Archive",
|
||||||
|
systemImage: viewModel.bookmarkDetail.isArchived ? "checkmark.circle" : "archivebox"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.disabled(viewModel.isLoading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var fontSettingsSheet: some View {
|
private var fontSettingsSheet: some View {
|
||||||
@ -291,18 +278,11 @@ struct BookmarkDetailView2: View {
|
|||||||
private func headerView(width: CGFloat) -> some View {
|
private func headerView(width: CGFloat) -> some View {
|
||||||
if !viewModel.bookmarkDetail.imageUrl.isEmpty {
|
if !viewModel.bookmarkDetail.imageUrl.isEmpty {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
// Background blur for images that don't fill
|
|
||||||
CachedAsyncImage(url: URL(string: viewModel.bookmarkDetail.imageUrl))
|
CachedAsyncImage(url: URL(string: viewModel.bookmarkDetail.imageUrl))
|
||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: width, height: headerHeight)
|
.frame(width: width, height: headerHeight)
|
||||||
.blur(radius: 30)
|
|
||||||
.clipped()
|
.clipped()
|
||||||
|
|
||||||
// Main image with fit
|
|
||||||
CachedAsyncImage(url: URL(string: viewModel.bookmarkDetail.imageUrl))
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(width: width, height: headerHeight)
|
|
||||||
|
|
||||||
// Zoom icon
|
// Zoom icon
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showingImageViewer = true
|
showingImageViewer = true
|
||||||
|
|||||||
@ -5,7 +5,6 @@ struct MainTabView: View {
|
|||||||
@State private var selectedTab: SidebarTab = .unread
|
@State private var selectedTab: SidebarTab = .unread
|
||||||
@State var selectedBookmark: Bookmark?
|
@State var selectedBookmark: Bookmark?
|
||||||
@StateObject private var playerUIState = PlayerUIState()
|
@StateObject private var playerUIState = PlayerUIState()
|
||||||
@State private var showReleaseNotes = false
|
|
||||||
|
|
||||||
// sizeClass
|
// sizeClass
|
||||||
@Environment(\.horizontalSizeClass)
|
@Environment(\.horizontalSizeClass)
|
||||||
@ -15,7 +14,6 @@ struct MainTabView: View {
|
|||||||
var verticalSizeClass
|
var verticalSizeClass
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
|
||||||
if UIDevice.isPhone {
|
if UIDevice.isPhone {
|
||||||
PhoneTabView()
|
PhoneTabView()
|
||||||
.environmentObject(playerUIState)
|
.environmentObject(playerUIState)
|
||||||
@ -24,20 +22,6 @@ struct MainTabView: View {
|
|||||||
.environmentObject(playerUIState)
|
.environmentObject(playerUIState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showReleaseNotes) {
|
|
||||||
ReleaseNotesView()
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
checkForNewVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func checkForNewVersion() {
|
|
||||||
if VersionManager.shared.isNewVersion {
|
|
||||||
showReleaseNotes = true
|
|
||||||
VersionManager.shared.markVersionAsSeen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ReleaseNotesView: View {
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
ScrollView {
|
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
|
||||||
if let attributedString = loadReleaseNotes() {
|
|
||||||
Text(attributedString)
|
|
||||||
.textSelection(.enabled)
|
|
||||||
.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() -> AttributedString? {
|
|
||||||
guard let url = Bundle.main.url(forResource: "RELEASE_NOTES", withExtension: "md"),
|
|
||||||
let markdownContent = try? String(contentsOf: url),
|
|
||||||
let attributedString = try? AttributedString(
|
|
||||||
markdown: markdownContent,
|
|
||||||
options: .init(interpretedSyntax: .full)
|
|
||||||
) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return attributedString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
ReleaseNotesView()
|
|
||||||
}
|
|
||||||
@ -9,7 +9,6 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SettingsGeneralView: View {
|
struct SettingsGeneralView: View {
|
||||||
@State private var viewModel: SettingsGeneralViewModel
|
@State private var viewModel: SettingsGeneralViewModel
|
||||||
@State private var showReleaseNotes = false
|
|
||||||
|
|
||||||
init(viewModel: SettingsGeneralViewModel = SettingsGeneralViewModel()) {
|
init(viewModel: SettingsGeneralViewModel = SettingsGeneralViewModel()) {
|
||||||
self.viewModel = viewModel
|
self.viewModel = viewModel
|
||||||
@ -23,24 +22,6 @@ struct SettingsGeneralView: View {
|
|||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
Text("General")
|
Text("General")
|
||||||
.font(.headline)
|
.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)
|
Toggle("Read Aloud Feature", isOn: $viewModel.enableTTS)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
.onChange(of: viewModel.enableTTS) {
|
.onChange(of: viewModel.enableTTS) {
|
||||||
@ -117,9 +98,6 @@ struct SettingsGeneralView: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showReleaseNotes) {
|
|
||||||
ReleaseNotesView()
|
|
||||||
}
|
|
||||||
.task {
|
.task {
|
||||||
await viewModel.loadGeneralSettings()
|
await viewModel.loadGeneralSettings()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,58 +51,3 @@ struct readeckApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct TestView: View {
|
|
||||||
var body: some View {
|
|
||||||
if #available(iOS 26.0, *) {
|
|
||||||
Text("hello")
|
|
||||||
.toolbar {
|
|
||||||
ToolbarSpacer(.flexible)
|
|
||||||
|
|
||||||
ToolbarItem {
|
|
||||||
Button {
|
|
||||||
|
|
||||||
} label: {
|
|
||||||
Label("Favorite", systemImage: "share")
|
|
||||||
.symbolVariant(.none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolbarSpacer(.fixed)
|
|
||||||
|
|
||||||
ToolbarItemGroup {
|
|
||||||
Button {
|
|
||||||
|
|
||||||
} label: {
|
|
||||||
Label("Favorite", systemImage: "heart")
|
|
||||||
.symbolVariant(.none)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Info", systemImage: "info") {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolbarItemGroup(placement: .bottomBar) {
|
|
||||||
Spacer()
|
|
||||||
Button {
|
|
||||||
|
|
||||||
} label: {
|
|
||||||
Label("Favorite", systemImage: "heart")
|
|
||||||
.symbolVariant(.none)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Info", systemImage: "info") {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.toolbar(removing: .title)
|
|
||||||
.ignoresSafeArea(edges: .top)
|
|
||||||
} else {
|
|
||||||
Text("hello1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
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() {}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user