Merge pull request #185 from omnivore-app/fix/ipad-refresh

iPad Refresh
This commit is contained in:
Satindar Dhillon 2022-03-04 13:13:24 -08:00 committed by GitHub
commit 3e2d5e082a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 78 deletions

View file

@ -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") }
)
}
}
}
}
}
}

View file

@ -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
)
}
}

View file

@ -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
}