mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #185 from omnivore-app/fix/ipad-refresh
iPad Refresh
This commit is contained in:
commit
3e2d5e082a
3 changed files with 141 additions and 78 deletions
|
|
@ -100,6 +100,8 @@ import Views
|
|||
}
|
||||
|
||||
struct HomeFeedView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
||||
let isCompact: Bool
|
||||
@Binding var searchQuery: String
|
||||
@Binding var selectedLinkItem: FeedItem?
|
||||
|
|
@ -125,6 +127,32 @@ import Views
|
|||
itemToSnooze: $itemToSnooze,
|
||||
viewModel: viewModel
|
||||
)
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
if #available(iOS 15.0, *) {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
},
|
||||
label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
|
||||
)
|
||||
.disabled(viewModel.isLoading)
|
||||
.opacity(viewModel.isLoading ? 0 : 1)
|
||||
.overlay {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
},
|
||||
label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,60 +19,122 @@ import Views
|
|||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
|
||||
var body: some View {
|
||||
if #available(macOS 12.0, *) {
|
||||
innerBody
|
||||
} else {
|
||||
innerBodyMac11
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 12.0, *)
|
||||
var innerBody: some View {
|
||||
List {
|
||||
Section {
|
||||
ForEach(viewModel.items) { item in
|
||||
if #available(macOS 12.0, *) {
|
||||
FeedCardNavigationLink(
|
||||
item: item,
|
||||
searchQuery: searchQuery,
|
||||
selectedLinkItem: $selectedLinkItem,
|
||||
viewModel: viewModel
|
||||
)
|
||||
.contextMenu {
|
||||
Button(action: {
|
||||
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived)
|
||||
}, label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
})
|
||||
Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: { Label("Delete Link", systemImage: "trash") }
|
||||
FeedCardNavigationLink(
|
||||
item: item,
|
||||
searchQuery: searchQuery,
|
||||
selectedLinkItem: $selectedLinkItem,
|
||||
viewModel: viewModel
|
||||
)
|
||||
.contextMenu {
|
||||
Button(action: {
|
||||
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived)
|
||||
}, label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
itemToSnooze = item
|
||||
snoozePresented = true
|
||||
} label: {
|
||||
Label { Text("Snooze") } icon: { Image.moon }
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Are you sure?", isPresented: $confirmationShown) {
|
||||
Button("Remove Link", role: .destructive) {
|
||||
if let itemToRemove = itemToRemove {
|
||||
withAnimation {
|
||||
viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
|
||||
self.itemToRemove = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
|
||||
}
|
||||
} else {
|
||||
FeedCardNavigationLink(
|
||||
item: item,
|
||||
searchQuery: searchQuery,
|
||||
selectedLinkItem: $selectedLinkItem,
|
||||
viewModel: viewModel
|
||||
})
|
||||
Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: { Label("Delete Link", systemImage: "trash") }
|
||||
)
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
itemToSnooze = item
|
||||
snoozePresented = true
|
||||
} label: {
|
||||
Label { Text("Snooze") } icon: { Image.moon }
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Are you sure?", isPresented: $confirmationShown) {
|
||||
Button("Remove Link", role: .destructive) {
|
||||
if let itemToRemove = itemToRemove {
|
||||
withAnimation {
|
||||
viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
|
||||
self.itemToRemove = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.isLoading {
|
||||
LoadingSection()
|
||||
}
|
||||
}
|
||||
.listStyle(PlainListStyle())
|
||||
.navigationTitle("Home")
|
||||
.searchable(
|
||||
text: $searchQuery,
|
||||
placement: .toolbar
|
||||
) {
|
||||
if searchQuery.isEmpty {
|
||||
Text("Inbox").searchCompletion("in:inbox ")
|
||||
Text("All").searchCompletion("in:all ")
|
||||
Text("Archived").searchCompletion("in:archive ")
|
||||
Text("Files").searchCompletion("type:file ")
|
||||
}
|
||||
}
|
||||
.onChange(of: searchQuery) { _ in
|
||||
// Maybe we should debounce this, but
|
||||
// it feels like it works ok without
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
}
|
||||
.onSubmit(of: .search) {
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
},
|
||||
label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
|
||||
)
|
||||
.disabled(viewModel.isLoading)
|
||||
.opacity(viewModel.isLoading ? 0 : 1)
|
||||
.overlay {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if viewModel.items.isEmpty {
|
||||
viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var innerBodyMac11: some View {
|
||||
List {
|
||||
Section {
|
||||
ForEach(viewModel.items) { item in
|
||||
FeedCardNavigationLink(
|
||||
item: item,
|
||||
searchQuery: searchQuery,
|
||||
selectedLinkItem: $selectedLinkItem,
|
||||
viewModel: viewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
searchIdx += 1
|
||||
|
||||
isLoading = true
|
||||
startNetworkActivityIndicator()
|
||||
|
||||
// Cache the viewer
|
||||
if dataService.currentViewer == nil {
|
||||
|
|
@ -64,10 +63,8 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
)
|
||||
.sink(
|
||||
receiveCompletion: { [weak self] completion in
|
||||
guard case let .failure(error) = completion else { return }
|
||||
guard case .failure = completion else { return }
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
print(error)
|
||||
},
|
||||
receiveValue: { [weak self] result in
|
||||
// Search results aren't guaranteed to return in order so this
|
||||
|
|
@ -82,7 +79,6 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
self?.isLoading = false
|
||||
self?.receivedIdx = thisSearchIdx
|
||||
self?.cursor = result.cursor
|
||||
stopNetworkActivityIndicator()
|
||||
}
|
||||
)
|
||||
.store(in: &subscriptions)
|
||||
|
|
@ -90,7 +86,6 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
|
||||
func setLinkArchived(dataService: DataService, linkId: String, archived: Bool) {
|
||||
isLoading = true
|
||||
startNetworkActivityIndicator()
|
||||
|
||||
// First remove the link from the internal list,
|
||||
// then make a call to remove it. The isLoading block should
|
||||
|
|
@ -103,15 +98,12 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
dataService.archiveLinkPublisher(itemID: linkId, archived: archived)
|
||||
.sink(
|
||||
receiveCompletion: { [weak self] completion in
|
||||
guard case let .failure(error) = completion else { return }
|
||||
guard case .failure = completion else { return }
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
print(error)
|
||||
NSNotification.operationFailed(message: archived ? "Failed to archive link" : "Failed to unarchive link")
|
||||
},
|
||||
receiveValue: { [weak self] _ in
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
Snackbar.show(message: archived ? "Link archived" : "Link moved to Inbox")
|
||||
}
|
||||
)
|
||||
|
|
@ -120,7 +112,6 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
|
||||
func removeLink(dataService: DataService, linkId: String) {
|
||||
isLoading = true
|
||||
startNetworkActivityIndicator()
|
||||
|
||||
if let itemIndex = items.firstIndex(where: { $0.id == linkId }) {
|
||||
items.remove(at: itemIndex)
|
||||
|
|
@ -131,12 +122,10 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
receiveCompletion: { [weak self] completion in
|
||||
guard case .failure = completion else { return }
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
Snackbar.show(message: "Failed to remove link")
|
||||
},
|
||||
receiveValue: { [weak self] _ in
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
Snackbar.show(message: "Link removed")
|
||||
}
|
||||
)
|
||||
|
|
@ -145,7 +134,6 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
|
||||
func snoozeUntil(dataService: DataService, linkId: String, until: Date, successMessage: String?) {
|
||||
isLoading = true
|
||||
startNetworkActivityIndicator()
|
||||
|
||||
if let itemIndex = items.firstIndex(where: { $0.id == linkId }) {
|
||||
items.remove(at: itemIndex)
|
||||
|
|
@ -157,15 +145,12 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
)
|
||||
.sink(
|
||||
receiveCompletion: { [weak self] completion in
|
||||
guard case let .failure(error) = completion else { return }
|
||||
guard case .failure = completion else { return }
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
print(error)
|
||||
NSNotification.operationFailed(message: "Failed to snooze")
|
||||
},
|
||||
receiveValue: { [weak self] _ in
|
||||
self?.isLoading = false
|
||||
stopNetworkActivityIndicator()
|
||||
if let message = successMessage {
|
||||
Snackbar.show(message: message)
|
||||
}
|
||||
|
|
@ -174,15 +159,3 @@ final class HomeFeedViewModel: ObservableObject {
|
|||
.store(in: &subscriptions)
|
||||
}
|
||||
}
|
||||
|
||||
private func startNetworkActivityIndicator() {
|
||||
#if os(iOS)
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = true
|
||||
#endif
|
||||
}
|
||||
|
||||
private func stopNetworkActivityIndicator() {
|
||||
#if os(iOS)
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = false
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue