iOS basic UI for recommending

This commit is contained in:
Jackson Harper
2022-12-05 17:45:06 +08:00
parent c03c6e2a51
commit f2a0c540a6
4 changed files with 178 additions and 15 deletions

View File

@ -1,8 +1,115 @@
//
// File.swift
//
//
// Created by Jackson Harper on 12/5/22.
//
import Models
import Services
import SwiftUI
import Views
import Foundation
@MainActor final class RecommendToViewModel: ObservableObject {
@Published var isLoading = false
@Published var networkError = true
@Published var recommendationGroups = [InternalRecommendationGroup]()
@Published var selectedGroups = [String]()
@Published var isRunning = false
@Published var showError = false
let pageID: String
init(pageID: String) {
self.pageID = pageID
}
func loadGroups(dataService: DataService) async {
isLoading = true
do {
recommendationGroups = try await dataService.recommendationGroups()
} catch {
networkError = true
}
isLoading = false
}
func recommend(dataService: DataService) async {
isRunning = true
do {
try await dataService.recommendPage(pageID: pageID, groupIDs: selectedGroups)
} catch {
showError = true
}
isRunning = false
}
}
struct RecommendToView: View {
var dataService: DataService
@StateObject var viewModel: RecommendToViewModel
@Environment(\.dismiss) private var dismiss
var nextButton: some View {
if viewModel.isRunning {
return AnyView(ProgressView())
} else {
return AnyView(Button(action: {
Task {
await viewModel.recommend(dataService: dataService)
Snackbar.show(message: "Recommendation sent")
dismiss()
}
}, label: {
Text("Next")
})
.disabled(viewModel.selectedGroups.isEmpty)
)
}
}
var body: some View {
VStack {
List {
ForEach(viewModel.recommendationGroups) { group in
HStack {
Text(group.name)
Spacer()
if viewModel.selectedGroups.contains(group.id) {
Image(systemName: "checkmark")
}
}
.contentShape(Rectangle())
.onTapGesture {
let idx = viewModel.selectedGroups.firstIndex(of: group.id)
if let idx = idx {
viewModel.selectedGroups.remove(at: idx)
} else {
viewModel.selectedGroups.append(group.id)
}
}
}
}
.listStyle(.grouped)
Spacer()
}
.alert(isPresented: $viewModel.showError) {
Alert(
title: Text("Error recommending this page"),
dismissButton: .cancel(Text("Ok")) {
viewModel.showError = false
}
)
}
.navigationBarTitle("Recommend To")
.navigationBarTitleDisplayMode(.inline)
.navigationViewStyle(.stack)
.navigationBarItems(leading: Button(action: {
dismiss()
}, label: { Text("Cancel") }),
trailing: nextButton)
.task {
await viewModel.loadGroups(dataService: dataService)
}
}
}

View File

@ -29,6 +29,7 @@ struct WebReaderContainerView: View {
@State private var bottomBarOpacity = 0.0
@State private var errorAlertMessage: String?
@State private var showErrorAlertMessage = false
@State private var displayRecommendSheet = false
@EnvironmentObject var dataService: DataService
@EnvironmentObject var audioController: AudioController
@ -219,6 +220,13 @@ struct WebReaderContainerView: View {
action: delete,
label: { Label("Delete", systemImage: "trash") }
)
Button(
action: {
// dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0)
displayRecommendSheet = true
},
label: { Label("Recommend", systemImage: "sparkles") }
)
}
}
@ -351,6 +359,14 @@ struct WebReaderContainerView: View {
showErrorAlertMessage = false
})
}
.formSheet(isPresented: $displayRecommendSheet) {
NavigationView {
RecommendToView(
dataService: dataService,
viewModel: RecommendToViewModel(pageID: item.unwrappedID)
)
}
}
.sheet(isPresented: $showHighlightAnnotationModal) {
HighlightAnnotationSheet(
annotation: $annotation,

View File

@ -1,8 +1,48 @@
//
// File.swift
//
//
// Created by Jackson Harper on 12/5/22.
//
import CoreData
import Foundation
import Models
import SwiftGraphQL
public extension DataService {
func recommendPage(pageID: String, groupIDs: [String]) async throws {
enum MutationResult {
case saved(taskNames: [String])
case error(errorMessage: String)
}
let selection = Selection<MutationResult, Unions.RecommendResult> {
try $0.on(
recommendError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
recommendSuccess: .init {
.saved(taskNames: try $0.taskNames())
}
)
}
let mutation = Selection.Mutation {
try $0.recommend(
input: .init(groupIds: groupIDs, pageId: pageID),
selection: selection
)
}
let path = appEnvironment.graphqlPath
let headers = networker.defaultHeaders
return try await withCheckedThrowingContinuation { continuation in
send(mutation, to: path, headers: headers) { queryResult in
guard let payload = try? queryResult.get() else {
continuation.resume(throwing: BasicError.message(messageText: "network error"))
return
}
switch payload.data {
case .saved:
continuation.resume()
case let .error(errorMessage: errorMessage):
continuation.resume(throwing: BasicError.message(messageText: errorMessage))
}
}
}
}
}

View File

@ -70,7 +70,7 @@ log(`Migrating to ${targetMigrationLabel}.\n`)
const logAppliedMigrations = (
appliedMigrations: Postgrator.Migration[]
): void => {
if (appliedMigrations.length > 0) {
if (appliedMigrations && appliedMigrations.length > 0) {
log(
`Applied ${chalk.green(
appliedMigrations.length.toString()