feat: Improve AddBookmarkView and loading animations
- Simplify AddBookmarkView header by removing large icon and title - Add clipboard monitoring with button directly under URL field - Improve clipboard detection logic with smart URL comparison - Add dismiss functionality for clipboard suggestions - Enhance loading animations in BookmarksView: - Better initial loading screen with centered animation - Use pull-to-refresh instead of overlay for reloading - Add 1-second delay after creating bookmark for server sync - Remove custom Close button styling for default appearance - Improve overall UX with more natural iOS patterns
This commit is contained in:
parent
03713230b0
commit
d036c2e658
@ -50,9 +50,6 @@
|
|||||||
},
|
},
|
||||||
"Add" : {
|
"Add" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Add a new link to your collection" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Add new tag..." : {
|
"Add new tag..." : {
|
||||||
|
|
||||||
@ -94,9 +91,6 @@
|
|||||||
},
|
},
|
||||||
"Clear cache" : {
|
"Clear cache" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Clipboard" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Close" : {
|
"Close" : {
|
||||||
|
|
||||||
@ -182,7 +176,7 @@
|
|||||||
"Labels" : {
|
"Labels" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Loading %@..." : {
|
"Loading %@" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Loading article..." : {
|
"Loading article..." : {
|
||||||
@ -235,6 +229,9 @@
|
|||||||
},
|
},
|
||||||
"Paste" : {
|
"Paste" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Please wait while we fetch your bookmarks..." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Preview" : {
|
"Preview" : {
|
||||||
|
|
||||||
@ -345,7 +342,7 @@
|
|||||||
"URL" : {
|
"URL" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"URL found:" : {
|
"URL in clipboard:" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Username" : {
|
"Username" : {
|
||||||
|
|||||||
@ -620,7 +620,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 = 11;
|
CURRENT_PROJECT_VERSION = 12;
|
||||||
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;
|
||||||
@ -664,7 +664,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 = 11;
|
CURRENT_PROJECT_VERSION = 12;
|
||||||
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;
|
||||||
|
|||||||
@ -19,24 +19,7 @@ struct AddBookmarkView: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// Scrollable Form Content
|
// Scrollable Form Content
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 24) {
|
VStack(spacing: 20) {
|
||||||
// Header
|
|
||||||
VStack(spacing: 8) {
|
|
||||||
Image(systemName: "bookmark.circle.fill")
|
|
||||||
.font(.system(size: 48))
|
|
||||||
.foregroundColor(.accentColor)
|
|
||||||
|
|
||||||
Text("New Bookmark")
|
|
||||||
.font(.title2)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
|
|
||||||
Text("Add a new link to your collection")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
}
|
|
||||||
.padding(.top, 20)
|
|
||||||
|
|
||||||
// Form Fields
|
// Form Fields
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
// URL Field
|
// URL Field
|
||||||
@ -58,6 +41,46 @@ struct AddBookmarkView: View {
|
|||||||
.keyboardType(.URL)
|
.keyboardType(.URL)
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
|
.onChange(of: viewModel.url) { _, _ in
|
||||||
|
viewModel.checkClipboard()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clipboard Button
|
||||||
|
if viewModel.showClipboardButton {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text("URL in clipboard:")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
Text(viewModel.clipboardURL ?? "")
|
||||||
|
.font(.subheadline)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.middle)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Button("Paste") {
|
||||||
|
viewModel.pasteFromClipboard()
|
||||||
|
}
|
||||||
|
.buttonStyle(SecondaryButtonStyle())
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
viewModel.dismissClipboard()
|
||||||
|
}) {
|
||||||
|
Image(systemName: "xmark.circle.fill")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(Color(.systemGray6))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||||
|
.transition(.opacity.combined(with: .move(edge: .top)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title Field
|
// Title Field
|
||||||
@ -98,38 +121,6 @@ struct AddBookmarkView: View {
|
|||||||
.padding(.top, 8)
|
.padding(.top, 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clipboard Section
|
|
||||||
if viewModel.clipboardURL != nil {
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
|
||||||
Label("Clipboard", systemImage: "doc.on.clipboard")
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
|
||||||
Text("URL found:")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
|
|
||||||
Text(viewModel.clipboardURL ?? "")
|
|
||||||
.font(.subheadline)
|
|
||||||
.lineLimit(2)
|
|
||||||
.truncationMode(.middle)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button("Paste") {
|
|
||||||
viewModel.pasteFromClipboard()
|
|
||||||
}
|
|
||||||
.buttonStyle(SecondaryButtonStyle())
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(Color(.systemGray6))
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
|
|
||||||
@ -183,6 +174,7 @@ struct AddBookmarkView: View {
|
|||||||
}
|
}
|
||||||
.background(Color(.systemBackground))
|
.background(Color(.systemBackground))
|
||||||
}
|
}
|
||||||
|
.navigationTitle("New Bookmark")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
@ -190,7 +182,6 @@ struct AddBookmarkView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
viewModel.clearForm()
|
viewModel.clearForm()
|
||||||
}
|
}
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert("Error", isPresented: $viewModel.showErrorAlert) {
|
.alert("Error", isPresented: $viewModel.showErrorAlert) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class AddBookmarkViewModel {
|
|||||||
var showErrorAlert: Bool = false
|
var showErrorAlert: Bool = false
|
||||||
var hasCreated: Bool = false
|
var hasCreated: Bool = false
|
||||||
var clipboardURL: String?
|
var clipboardURL: String?
|
||||||
|
var showClipboardButton: Bool = false
|
||||||
|
|
||||||
var isValid: Bool {
|
var isValid: Bool {
|
||||||
!url.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
|
!url.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
|
||||||
@ -68,20 +69,35 @@ class AddBookmarkViewModel {
|
|||||||
guard let clipboardString = UIPasteboard.general.string,
|
guard let clipboardString = UIPasteboard.general.string,
|
||||||
URL(string: clipboardString) != nil else {
|
URL(string: clipboardString) != nil else {
|
||||||
clipboardURL = nil
|
clipboardURL = nil
|
||||||
|
showClipboardButton = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clipboardURL = clipboardString
|
// Only show clipboard button if the URL is different from current URL
|
||||||
|
let currentURL = url.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if clipboardString != currentURL {
|
||||||
|
clipboardURL = clipboardString
|
||||||
|
showClipboardButton = true
|
||||||
|
} else {
|
||||||
|
showClipboardButton = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pasteFromClipboard() {
|
func pasteFromClipboard() {
|
||||||
guard let clipboardURL = clipboardURL else { return }
|
guard let clipboardURL = clipboardURL else { return }
|
||||||
url = clipboardURL
|
url = clipboardURL
|
||||||
|
showClipboardButton = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func dismissClipboard() {
|
||||||
|
showClipboardButton = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearForm() {
|
func clearForm() {
|
||||||
url = ""
|
url = ""
|
||||||
title = ""
|
title = ""
|
||||||
labelsText = ""
|
labelsText = ""
|
||||||
|
clipboardURL = nil
|
||||||
|
showClipboardButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ struct BookmarkLabelsView: View {
|
|||||||
|
|
||||||
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.primary)
|
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.primary)
|
||||||
UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.primary).withAlphaComponent(0.2)
|
UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.primary).withAlphaComponent(0.2)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|||||||
@ -38,7 +38,31 @@ struct BookmarksView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if viewModel.isLoading && viewModel.bookmarks?.bookmarks.isEmpty == true {
|
if viewModel.isLoading && viewModel.bookmarks?.bookmarks.isEmpty == true {
|
||||||
ProgressView("Loading \(state.displayName)...")
|
VStack(spacing: 20) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
ProgressView()
|
||||||
|
.scaleEffect(1.3)
|
||||||
|
.tint(.accentColor)
|
||||||
|
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
Text("Loading \(state.displayName)")
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
|
||||||
|
Text("Please wait while we fetch your bookmarks...")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background(Color(R.color.bookmark_list_bg))
|
||||||
} else {
|
} else {
|
||||||
List {
|
List {
|
||||||
ForEach(viewModel.bookmarks?.bookmarks ?? [], id: \.id) { bookmark in
|
ForEach(viewModel.bookmarks?.bookmarks ?? [], id: \.id) { bookmark in
|
||||||
@ -169,7 +193,10 @@ struct BookmarksView: View {
|
|||||||
// Refresh bookmarks when sheet is dismissed
|
// Refresh bookmarks when sheet is dismissed
|
||||||
if oldValue && !newValue {
|
if oldValue && !newValue {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.loadBookmarks(state: state, type: type)
|
// Wait a bit for the server to process the new bookmark
|
||||||
|
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
|
||||||
|
|
||||||
|
await viewModel.refreshBookmarks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user