show subscriptions in list view
This commit is contained in:
@ -5,41 +5,17 @@ import Views
|
||||
|
||||
@MainActor final class SubscriptionsViewModel: ObservableObject {
|
||||
@Published var isLoading = false
|
||||
@Published var emails = [NewsletterEmail]()
|
||||
@Published var subscriptions = [Subscription]()
|
||||
@Published var popularSubscriptions = [Subscription]()
|
||||
@Published var hasNetworkError = false
|
||||
|
||||
func loadSubscriptions(dataService _: DataService) async {
|
||||
func loadSubscriptions(dataService: DataService) async {
|
||||
isLoading = true
|
||||
|
||||
// if let subscriptions = try? await dataService.subscriptions() {
|
||||
// await dataService.viewContext.perform { [weak self] in
|
||||
// self?.emails = objectIDs.compactMap { dataService.viewContext.object(with: $0) as? NewsletterEmail }
|
||||
// }
|
||||
// }
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
func loadEmails(dataService: DataService) async {
|
||||
isLoading = true
|
||||
|
||||
if let objectIDs = try? await dataService.newsletterEmails() {
|
||||
await dataService.viewContext.perform { [weak self] in
|
||||
self?.emails = objectIDs.compactMap { dataService.viewContext.object(with: $0) as? NewsletterEmail }
|
||||
}
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
func createEmail(dataService: DataService) async {
|
||||
isLoading = true
|
||||
|
||||
if let objectID = try? await dataService.createNewsletter() {
|
||||
await dataService.viewContext.perform { [weak self] in
|
||||
if let item = dataService.viewContext.object(with: objectID) as? NewsletterEmail {
|
||||
self?.emails.insert(item, at: 0)
|
||||
}
|
||||
}
|
||||
do {
|
||||
subscriptions = try await dataService.subscriptions()
|
||||
} catch {
|
||||
hasNetworkError = true
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
@ -49,7 +25,7 @@ import Views
|
||||
struct SubscriptionsView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@StateObject var viewModel = SubscriptionsViewModel()
|
||||
let footerText = "Add PDFs to your library, or subscribe to emails using an Omnivore email address."
|
||||
let footerText = "Describe subscriptions here."
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
@ -64,50 +40,30 @@ struct SubscriptionsView: View {
|
||||
.listStyle(InsetListStyle())
|
||||
#endif
|
||||
}
|
||||
.task { await viewModel.loadEmails(dataService: dataService) }
|
||||
.task { await viewModel.loadSubscriptions(dataService: dataService) }
|
||||
}
|
||||
|
||||
private var innerBody: some View {
|
||||
Group {
|
||||
Section(footer: Text(footerText)) {
|
||||
ForEach(viewModel.subscriptions, id: \.subscriptionID) { subscription in
|
||||
Button(
|
||||
action: {
|
||||
Task { await viewModel.createEmail(dataService: dataService) }
|
||||
},
|
||||
label: {
|
||||
HStack {
|
||||
Image(systemName: "plus.circle.fill").foregroundColor(.green)
|
||||
Text("Create a new email address")
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
action: {},
|
||||
label: { Text(subscription.name) }
|
||||
)
|
||||
.disabled(viewModel.isLoading)
|
||||
}
|
||||
|
||||
if !viewModel.emails.isEmpty {
|
||||
Section(header: Text("Existing Emails (Tap to copy)")) {
|
||||
ForEach(viewModel.emails) { newsletterEmail in
|
||||
Button(
|
||||
action: {
|
||||
#if os(iOS)
|
||||
UIPasteboard.general.string = newsletterEmail.email
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
let pasteBoard = NSPasteboard.general
|
||||
pasteBoard.clearContents()
|
||||
pasteBoard.writeObjects([newsletterEmail.unwrappedEmail as NSString])
|
||||
#endif
|
||||
|
||||
Snackbar.show(message: "Email copied")
|
||||
},
|
||||
label: { Text(newsletterEmail.unwrappedEmail) }
|
||||
)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
Button(
|
||||
role: .destructive,
|
||||
action: {
|
||||
// itemToRemove = item
|
||||
// confirmationShown = true
|
||||
},
|
||||
label: {
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Emails")
|
||||
.navigationTitle("Subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ public enum AppEnvironment: String {
|
||||
public static let initialAppEnvironment: AppEnvironment = {
|
||||
#if DEBUG
|
||||
#if targetEnvironment(simulator)
|
||||
return .demo // could also return .local here
|
||||
return .prod // could also return .local here
|
||||
#else
|
||||
return .demo
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
import Foundation
|
||||
|
||||
public struct Subscription {
|
||||
public let createdAt: Date?
|
||||
public let description: String?
|
||||
public let subscriptionID: String
|
||||
public let name: String
|
||||
public let newsletterEmailAddress: String
|
||||
public let status: SubscriptionStatus
|
||||
public let unsubscribeHttpUrl: String?
|
||||
public let unsubscribeMailTo: String?
|
||||
public let updatedAt: Date?
|
||||
public let url: String?
|
||||
|
||||
public init(
|
||||
createdAt: Date?,
|
||||
description: String?,
|
||||
subscriptionID: String,
|
||||
name: String,
|
||||
newsletterEmailAddress: String,
|
||||
status: SubscriptionStatus,
|
||||
unsubscribeHttpUrl: String?,
|
||||
unsubscribeMailTo: String?,
|
||||
updatedAt: Date?,
|
||||
url: String?
|
||||
) {
|
||||
self.createdAt = createdAt
|
||||
self.description = description
|
||||
self.subscriptionID = subscriptionID
|
||||
self.name = name
|
||||
self.newsletterEmailAddress = newsletterEmailAddress
|
||||
self.status = status
|
||||
self.unsubscribeHttpUrl = unsubscribeHttpUrl
|
||||
self.unsubscribeMailTo = unsubscribeMailTo
|
||||
self.updatedAt = updatedAt
|
||||
self.url = url
|
||||
}
|
||||
}
|
||||
|
||||
public enum SubscriptionStatus {
|
||||
case active
|
||||
case deleted
|
||||
case unsubscribed
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
public extension DataService {
|
||||
func subscriptions() async throws -> [Subscription] {
|
||||
enum QueryResult {
|
||||
case success(result: [Subscription])
|
||||
case error(error: String)
|
||||
}
|
||||
|
||||
let subsciptionSelection = Selection.Subscription {
|
||||
Subscription(
|
||||
createdAt: try $0.createdAt().value,
|
||||
description: try $0.description(),
|
||||
subscriptionID: try $0.id(),
|
||||
name: try $0.name(),
|
||||
newsletterEmailAddress: try $0.newsletterEmail(),
|
||||
status: try SubscriptionStatus.make(from: $0.status()),
|
||||
unsubscribeHttpUrl: try $0.unsubscribeHttpUrl(),
|
||||
unsubscribeMailTo: try $0.unsubscribeMailTo(),
|
||||
updatedAt: try $0.updatedAt().value,
|
||||
url: try $0.url()
|
||||
)
|
||||
}
|
||||
|
||||
let selection = Selection<QueryResult, Unions.SubscriptionsResult> {
|
||||
try $0.on(
|
||||
subscriptionsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
subscriptionsSuccess: .init {
|
||||
QueryResult.success(result: try $0.subscriptions(selection: subsciptionSelection.list))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
try $0.subscriptions(selection: selection)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
let headers = networker.defaultHeaders
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(query, to: path, headers: headers) { queryResult in
|
||||
guard let payload = try? queryResult.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network request failed"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case let .success(result: result):
|
||||
continuation.resume(returning: result)
|
||||
case .error:
|
||||
continuation.resume(throwing: BasicError.message(messageText: "Subscriptions fetch error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SubscriptionStatus {
|
||||
static func make(from status: Enums.SubscriptionStatus) -> SubscriptionStatus {
|
||||
switch status {
|
||||
case .active:
|
||||
return .active
|
||||
case .deleted:
|
||||
return .deleted
|
||||
case .unsubscribed:
|
||||
return .unsubscribed
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user