mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #714 from omnivore-app/subscriptions-modal
Subscriptions Screen [iOS]
This commit is contained in:
commit
a9b70e12e3
8 changed files with 1085 additions and 3 deletions
|
|
@ -78,6 +78,10 @@ struct ProfileView: View {
|
|||
NavigationLink(destination: NewsletterEmailsView()) {
|
||||
Text("Emails")
|
||||
}
|
||||
|
||||
NavigationLink(destination: SubscriptionsView()) {
|
||||
Text("Subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
|
|
|
|||
140
apple/OmnivoreKit/Sources/App/Views/Profile/Subscriptions.swift
Normal file
140
apple/OmnivoreKit/Sources/App/Views/Profile/Subscriptions.swift
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Views
|
||||
|
||||
@MainActor final class SubscriptionsViewModel: ObservableObject {
|
||||
@Published var isLoading = true
|
||||
@Published var subscriptions = [Subscription]()
|
||||
@Published var popularSubscriptions = [Subscription]()
|
||||
@Published var hasNetworkError = false
|
||||
@Published var subscriptionNameToCancel: String?
|
||||
|
||||
func loadSubscriptions(dataService: DataService) async {
|
||||
isLoading = true
|
||||
|
||||
do {
|
||||
subscriptions = try await dataService.subscriptions()
|
||||
} catch {
|
||||
hasNetworkError = true
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
func cancelSubscription(dataService: DataService) async -> Bool {
|
||||
guard let subscriptionName = subscriptionNameToCancel else { return false }
|
||||
|
||||
do {
|
||||
try await dataService.deleteSubscription(subscriptionName: subscriptionName)
|
||||
let index = subscriptions.firstIndex { $0.name == subscriptionName }
|
||||
if let index = index {
|
||||
subscriptions.remove(at: index)
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
appLogger.debug("failed to remove subscription")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SubscriptionsView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@StateObject var viewModel = SubscriptionsViewModel()
|
||||
@State private var deleteConfirmationShown = false
|
||||
@State private var progressViewOpacity = 0.0
|
||||
|
||||
var body: some View {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
.opacity(progressViewOpacity)
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
|
||||
progressViewOpacity = 1
|
||||
}
|
||||
}
|
||||
.task { await viewModel.loadSubscriptions(dataService: dataService) }
|
||||
} else if viewModel.hasNetworkError {
|
||||
VStack {
|
||||
Text("Sorry, we were unable to retrieve your subscriptions.").multilineTextAlignment(.center)
|
||||
Button(
|
||||
action: { Task { await viewModel.loadSubscriptions(dataService: dataService) } },
|
||||
label: { Text("Retry") }
|
||||
)
|
||||
.buttonStyle(RoundedRectButtonStyle())
|
||||
}
|
||||
} else {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
Form {
|
||||
innerBody
|
||||
}
|
||||
#elseif os(macOS)
|
||||
List {
|
||||
innerBody
|
||||
}
|
||||
.listStyle(InsetListStyle())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var innerBody: some View {
|
||||
Group {
|
||||
ForEach(viewModel.subscriptions, id: \.subscriptionID) { subscription in
|
||||
SubscriptionCell(subscription: subscription)
|
||||
.swipeActions(edge: .trailing) {
|
||||
Button(
|
||||
role: .destructive,
|
||||
action: {
|
||||
deleteConfirmationShown = true
|
||||
viewModel.subscriptionNameToCancel = subscription.name
|
||||
},
|
||||
label: {
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Are you sure you want to cancel this subscription?", isPresented: $deleteConfirmationShown) {
|
||||
Button("Yes", role: .destructive) {
|
||||
Task {
|
||||
let unsubscribed = await viewModel.cancelSubscription(dataService: dataService)
|
||||
Snackbar.show(message: unsubscribed ? "Subscription cancelled." : "Could not unsubscribe.")
|
||||
}
|
||||
}
|
||||
Button("No", role: .cancel) {
|
||||
viewModel.subscriptionNameToCancel = nil
|
||||
}
|
||||
}
|
||||
.navigationTitle("Subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
struct SubscriptionCell: View {
|
||||
let subscription: Subscription
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(subscription.name)
|
||||
.font(.appCallout)
|
||||
.lineSpacing(1.25)
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
if let updatedDate = subscription.updatedAt {
|
||||
Text("Last received: \(updatedDate.formatted())")
|
||||
.font(.appCaption)
|
||||
.foregroundColor(.appGrayText)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(.vertical, 8)
|
||||
.frame(minHeight: 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
import Models
|
||||
import OSLog
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Utils
|
||||
import Views
|
||||
|
||||
let appLogger = Logger(subsystem: "app.omnivore", category: "app-package")
|
||||
|
||||
public struct RootView: View {
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@StateObject private var viewModel = RootViewModel()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -20,6 +20,136 @@ extension Objects.Subscription: GraphQLWebSocketOperation {
|
|||
// MARK: - Objects
|
||||
|
||||
enum Objects {}
|
||||
extension Objects {
|
||||
struct AddPopularReadError {
|
||||
let __typename: TypeName = .addPopularReadError
|
||||
let errorCodes: [String: [Enums.AddPopularReadErrorCode]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case addPopularReadError = "AddPopularReadError"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.AddPopularReadError: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.AddPopularReadErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.AddPopularReadError {
|
||||
func errorCodes() throws -> [Enums.AddPopularReadErrorCode] {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "errorCodes",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.errorCodes[field.alias!] {
|
||||
return data
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias AddPopularReadError<T> = Selection<T, Objects.AddPopularReadError>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct AddPopularReadSuccess {
|
||||
let __typename: TypeName = .addPopularReadSuccess
|
||||
let pageId: [String: String]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case addPopularReadSuccess = "AddPopularReadSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.AddPopularReadSuccess: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "pageId":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pageId = map["pageId"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.AddPopularReadSuccess {
|
||||
func pageId() throws -> String {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "pageId",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.pageId[field.alias!] {
|
||||
return data
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return String.mockValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias AddPopularReadSuccess<T> = Selection<T, Objects.AddPopularReadSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct ArchiveLinkError {
|
||||
let __typename: TypeName = .archiveLinkError
|
||||
|
|
@ -6691,6 +6821,7 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
extension Objects {
|
||||
struct Mutation {
|
||||
let __typename: TypeName = .mutation
|
||||
let addPopularRead: [String: Unions.AddPopularReadResult]
|
||||
let createArticle: [String: Unions.CreateArticleResult]
|
||||
let createArticleSavingRequest: [String: Unions.CreateArticleSavingRequestResult]
|
||||
let createHighlight: [String: Unions.CreateHighlightResult]
|
||||
|
|
@ -6725,11 +6856,13 @@ extension Objects {
|
|||
let setShareHighlight: [String: Unions.SetShareHighlightResult]
|
||||
let setUserPersonalization: [String: Unions.SetUserPersonalizationResult]
|
||||
let signup: [String: Unions.SignupResult]
|
||||
let subscribe: [String: Unions.SubscribeResult]
|
||||
let unsubscribe: [String: Unions.UnsubscribeResult]
|
||||
let updateHighlight: [String: Unions.UpdateHighlightResult]
|
||||
let updateHighlightReply: [String: Unions.UpdateHighlightReplyResult]
|
||||
let updateLabel: [String: Unions.UpdateLabelResult]
|
||||
let updateLinkShareInfo: [String: Unions.UpdateLinkShareInfoResult]
|
||||
let updatePage: [String: Unions.UpdatePageResult]
|
||||
let updateReminder: [String: Unions.UpdateReminderResult]
|
||||
let updateSharedComment: [String: Unions.UpdateSharedCommentResult]
|
||||
let updateUser: [String: Unions.UpdateUserResult]
|
||||
|
|
@ -6754,6 +6887,10 @@ extension Objects.Mutation: Decodable {
|
|||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "addPopularRead":
|
||||
if let value = try container.decode(Unions.AddPopularReadResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "createArticle":
|
||||
if let value = try container.decode(Unions.CreateArticleResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
|
|
@ -6890,6 +7027,10 @@ extension Objects.Mutation: Decodable {
|
|||
if let value = try container.decode(Unions.SignupResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "subscribe":
|
||||
if let value = try container.decode(Unions.SubscribeResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "unsubscribe":
|
||||
if let value = try container.decode(Unions.UnsubscribeResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
|
|
@ -6910,6 +7051,10 @@ extension Objects.Mutation: Decodable {
|
|||
if let value = try container.decode(Unions.UpdateLinkShareInfoResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "updatePage":
|
||||
if let value = try container.decode(Unions.UpdatePageResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "updateReminder":
|
||||
if let value = try container.decode(Unions.UpdateReminderResult?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
|
|
@ -6940,6 +7085,7 @@ extension Objects.Mutation: Decodable {
|
|||
}
|
||||
}
|
||||
|
||||
addPopularRead = map["addPopularRead"]
|
||||
createArticle = map["createArticle"]
|
||||
createArticleSavingRequest = map["createArticleSavingRequest"]
|
||||
createHighlight = map["createHighlight"]
|
||||
|
|
@ -6974,11 +7120,13 @@ extension Objects.Mutation: Decodable {
|
|||
setShareHighlight = map["setShareHighlight"]
|
||||
setUserPersonalization = map["setUserPersonalization"]
|
||||
signup = map["signup"]
|
||||
subscribe = map["subscribe"]
|
||||
unsubscribe = map["unsubscribe"]
|
||||
updateHighlight = map["updateHighlight"]
|
||||
updateHighlightReply = map["updateHighlightReply"]
|
||||
updateLabel = map["updateLabel"]
|
||||
updateLinkShareInfo = map["updateLinkShareInfo"]
|
||||
updatePage = map["updatePage"]
|
||||
updateReminder = map["updateReminder"]
|
||||
updateSharedComment = map["updateSharedComment"]
|
||||
updateUser = map["updateUser"]
|
||||
|
|
@ -6988,6 +7136,25 @@ extension Objects.Mutation: Decodable {
|
|||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.Mutation {
|
||||
func addPopularRead<Type>(name: String, selection: Selection<Type, Unions.AddPopularReadResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "addPopularRead",
|
||||
arguments: [Argument(name: "name", type: "String!", value: name)],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.addPopularRead[field.alias!] {
|
||||
return try selection.decode(data: data)
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
|
||||
func createArticle<Type>(input: InputObjects.CreateArticleInput, selection: Selection<Type, Unions.CreateArticleResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "createArticle",
|
||||
|
|
@ -7634,6 +7801,25 @@ extension Fields where TypeLock == Objects.Mutation {
|
|||
}
|
||||
}
|
||||
|
||||
func subscribe<Type>(name: String, selection: Selection<Type, Unions.SubscribeResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "subscribe",
|
||||
arguments: [Argument(name: "name", type: "String!", value: name)],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.subscribe[field.alias!] {
|
||||
return try selection.decode(data: data)
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe<Type>(name: String, selection: Selection<Type, Unions.UnsubscribeResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "unsubscribe",
|
||||
|
|
@ -7729,6 +7915,25 @@ extension Fields where TypeLock == Objects.Mutation {
|
|||
}
|
||||
}
|
||||
|
||||
func updatePage<Type>(input: InputObjects.UpdatePageInput, selection: Selection<Type, Unions.UpdatePageResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "updatePage",
|
||||
arguments: [Argument(name: "input", type: "UpdatePageInput!", value: input)],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.updatePage[field.alias!] {
|
||||
return try selection.decode(data: data)
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
|
||||
func updateReminder<Type>(input: InputObjects.UpdateReminderInput, selection: Selection<Type, Unions.UpdateReminderResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "updateReminder",
|
||||
|
|
@ -10225,6 +10430,7 @@ extension Objects {
|
|||
let readingProgressAnchorIndex: [String: Int]
|
||||
let readingProgressPercent: [String: Double]
|
||||
let shortId: [String: String]
|
||||
let siteName: [String: String]
|
||||
let slug: [String: String]
|
||||
let state: [String: Enums.ArticleSavingRequestStatus]
|
||||
let subscription: [String: String]
|
||||
|
|
@ -10324,6 +10530,10 @@ extension Objects.SearchItem: Decodable {
|
|||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "siteName":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "slug":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
|
|
@ -10384,6 +10594,7 @@ extension Objects.SearchItem: Decodable {
|
|||
readingProgressAnchorIndex = map["readingProgressAnchorIndex"]
|
||||
readingProgressPercent = map["readingProgressPercent"]
|
||||
shortId = map["shortId"]
|
||||
siteName = map["siteName"]
|
||||
slug = map["slug"]
|
||||
state = map["state"]
|
||||
subscription = map["subscription"]
|
||||
|
|
@ -10682,6 +10893,21 @@ extension Fields where TypeLock == Objects.SearchItem {
|
|||
}
|
||||
}
|
||||
|
||||
func siteName() throws -> String? {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "siteName",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
return data.siteName[field.alias!]
|
||||
case .mocking:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func slug() throws -> String {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "slug",
|
||||
|
|
@ -12332,6 +12558,137 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
typealias SignupSuccess<T> = Selection<T, Objects.SignupSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct SubscribeError {
|
||||
let __typename: TypeName = .subscribeError
|
||||
let errorCodes: [String: [Enums.SubscribeErrorCode]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case subscribeError = "SubscribeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.SubscribeError: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.SubscribeErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.SubscribeError {
|
||||
func errorCodes() throws -> [Enums.SubscribeErrorCode] {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "errorCodes",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.errorCodes[field.alias!] {
|
||||
return data
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias SubscribeError<T> = Selection<T, Objects.SubscribeError>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct SubscribeSuccess {
|
||||
let __typename: TypeName = .subscribeSuccess
|
||||
let subscriptions: [String: [Objects.Subscription]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case subscribeSuccess = "SubscribeSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.SubscribeSuccess: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "subscriptions":
|
||||
if let value = try container.decode([Objects.Subscription]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
subscriptions = map["subscriptions"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.SubscribeSuccess {
|
||||
func subscriptions<Type>(selection: Selection<Type, [Objects.Subscription]>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "subscriptions",
|
||||
arguments: [],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.subscriptions[field.alias!] {
|
||||
return try selection.decode(data: data)
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias SubscribeSuccess<T> = Selection<T, Objects.SubscribeSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct Subscription {
|
||||
let __typename: TypeName = .subscription
|
||||
|
|
@ -13386,6 +13743,137 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
typealias UpdateLinkShareInfoSuccess<T> = Selection<T, Objects.UpdateLinkShareInfoSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct UpdatePageError {
|
||||
let __typename: TypeName = .updatePageError
|
||||
let errorCodes: [String: [Enums.UpdatePageErrorCode]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case updatePageError = "UpdatePageError"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.UpdatePageError: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.UpdatePageErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.UpdatePageError {
|
||||
func errorCodes() throws -> [Enums.UpdatePageErrorCode] {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "errorCodes",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.errorCodes[field.alias!] {
|
||||
return data
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias UpdatePageError<T> = Selection<T, Objects.UpdatePageError>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct UpdatePageSuccess {
|
||||
let __typename: TypeName = .updatePageSuccess
|
||||
let updatedPage: [String: Objects.Page]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case updatePageSuccess = "UpdatePageSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.UpdatePageSuccess: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "updatedPage":
|
||||
if let value = try container.decode(Objects.Page?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
updatedPage = map["updatedPage"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.UpdatePageSuccess {
|
||||
func updatedPage<Type>(selection: Selection<Type, Objects.Page>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "updatedPage",
|
||||
arguments: [],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.updatedPage[field.alias!] {
|
||||
return try selection.decode(data: data)
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
case .mocking:
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias UpdatePageSuccess<T> = Selection<T, Objects.UpdatePageSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct UpdateReminderError {
|
||||
let __typename: TypeName = .updateReminderError
|
||||
|
|
@ -14891,6 +15379,80 @@ enum Interfaces {}
|
|||
// MARK: - Unions
|
||||
|
||||
enum Unions {}
|
||||
extension Unions {
|
||||
struct AddPopularReadResult {
|
||||
let __typename: TypeName
|
||||
let errorCodes: [String: [Enums.AddPopularReadErrorCode]]
|
||||
let pageId: [String: String]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case addPopularReadError = "AddPopularReadError"
|
||||
case addPopularReadSuccess = "AddPopularReadSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Unions.AddPopularReadResult: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.AddPopularReadErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "pageId":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
__typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
pageId = map["pageId"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Unions.AddPopularReadResult {
|
||||
func on<Type>(addPopularReadError: Selection<Type, Objects.AddPopularReadError>, addPopularReadSuccess: Selection<Type, Objects.AddPopularReadSuccess>) throws -> Type {
|
||||
select([GraphQLField.fragment(type: "AddPopularReadError", selection: addPopularReadError.selection), GraphQLField.fragment(type: "AddPopularReadSuccess", selection: addPopularReadSuccess.selection)])
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
switch data.__typename {
|
||||
case .addPopularReadError:
|
||||
let data = Objects.AddPopularReadError(errorCodes: data.errorCodes)
|
||||
return try addPopularReadError.decode(data: data)
|
||||
case .addPopularReadSuccess:
|
||||
let data = Objects.AddPopularReadSuccess(pageId: data.pageId)
|
||||
return try addPopularReadSuccess.decode(data: data)
|
||||
}
|
||||
case .mocking:
|
||||
return addPopularReadError.mock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias AddPopularReadResult<T> = Selection<T, Unions.AddPopularReadResult>
|
||||
}
|
||||
|
||||
extension Unions {
|
||||
struct ArchiveLinkResult {
|
||||
let __typename: TypeName
|
||||
|
|
@ -18065,6 +18627,80 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
typealias SignupResult<T> = Selection<T, Unions.SignupResult>
|
||||
}
|
||||
|
||||
extension Unions {
|
||||
struct SubscribeResult {
|
||||
let __typename: TypeName
|
||||
let errorCodes: [String: [Enums.SubscribeErrorCode]]
|
||||
let subscriptions: [String: [Objects.Subscription]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case subscribeError = "SubscribeError"
|
||||
case subscribeSuccess = "SubscribeSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Unions.SubscribeResult: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.SubscribeErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "subscriptions":
|
||||
if let value = try container.decode([Objects.Subscription]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
__typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
subscriptions = map["subscriptions"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Unions.SubscribeResult {
|
||||
func on<Type>(subscribeError: Selection<Type, Objects.SubscribeError>, subscribeSuccess: Selection<Type, Objects.SubscribeSuccess>) throws -> Type {
|
||||
select([GraphQLField.fragment(type: "SubscribeError", selection: subscribeError.selection), GraphQLField.fragment(type: "SubscribeSuccess", selection: subscribeSuccess.selection)])
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
switch data.__typename {
|
||||
case .subscribeError:
|
||||
let data = Objects.SubscribeError(errorCodes: data.errorCodes)
|
||||
return try subscribeError.decode(data: data)
|
||||
case .subscribeSuccess:
|
||||
let data = Objects.SubscribeSuccess(subscriptions: data.subscriptions)
|
||||
return try subscribeSuccess.decode(data: data)
|
||||
}
|
||||
case .mocking:
|
||||
return subscribeError.mock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias SubscribeResult<T> = Selection<T, Unions.SubscribeResult>
|
||||
}
|
||||
|
||||
extension Unions {
|
||||
struct SubscriptionsResult {
|
||||
let __typename: TypeName
|
||||
|
|
@ -18509,6 +19145,80 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
typealias UpdateLinkShareInfoResult<T> = Selection<T, Unions.UpdateLinkShareInfoResult>
|
||||
}
|
||||
|
||||
extension Unions {
|
||||
struct UpdatePageResult {
|
||||
let __typename: TypeName
|
||||
let errorCodes: [String: [Enums.UpdatePageErrorCode]]
|
||||
let updatedPage: [String: Objects.Page]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case updatePageError = "UpdatePageError"
|
||||
case updatePageSuccess = "UpdatePageSuccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Unions.UpdatePageResult: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "errorCodes":
|
||||
if let value = try container.decode([Enums.UpdatePageErrorCode]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "updatedPage":
|
||||
if let value = try container.decode(Objects.Page?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
__typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
|
||||
|
||||
errorCodes = map["errorCodes"]
|
||||
updatedPage = map["updatedPage"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Unions.UpdatePageResult {
|
||||
func on<Type>(updatePageError: Selection<Type, Objects.UpdatePageError>, updatePageSuccess: Selection<Type, Objects.UpdatePageSuccess>) throws -> Type {
|
||||
select([GraphQLField.fragment(type: "UpdatePageError", selection: updatePageError.selection), GraphQLField.fragment(type: "UpdatePageSuccess", selection: updatePageSuccess.selection)])
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
switch data.__typename {
|
||||
case .updatePageError:
|
||||
let data = Objects.UpdatePageError(errorCodes: data.errorCodes)
|
||||
return try updatePageError.decode(data: data)
|
||||
case .updatePageSuccess:
|
||||
let data = Objects.UpdatePageSuccess(updatedPage: data.updatedPage)
|
||||
return try updatePageSuccess.decode(data: data)
|
||||
}
|
||||
case .mocking:
|
||||
return updatePageError.mock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias UpdatePageResult<T> = Selection<T, Unions.UpdatePageResult>
|
||||
}
|
||||
|
||||
extension Unions {
|
||||
struct UpdateReminderResult {
|
||||
let __typename: TypeName
|
||||
|
|
@ -19048,6 +19758,17 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
// MARK: - Enums
|
||||
|
||||
enum Enums {}
|
||||
extension Enums {
|
||||
/// AddPopularReadErrorCode
|
||||
enum AddPopularReadErrorCode: String, CaseIterable, Codable {
|
||||
case badRequest = "BAD_REQUEST"
|
||||
|
||||
case notFound = "NOT_FOUND"
|
||||
|
||||
case unauthorized = "UNAUTHORIZED"
|
||||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// ArchiveLinkErrorCode
|
||||
enum ArchiveLinkErrorCode: String, CaseIterable, Codable {
|
||||
|
|
@ -19551,6 +20272,8 @@ extension Enums {
|
|||
extension Enums {
|
||||
/// SortBy
|
||||
enum SortBy: String, CaseIterable, Codable {
|
||||
case publishedAt = "PUBLISHED_AT"
|
||||
|
||||
case savedAt = "SAVED_AT"
|
||||
|
||||
case score = "SCORE"
|
||||
|
|
@ -19568,6 +20291,19 @@ extension Enums {
|
|||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// SubscribeErrorCode
|
||||
enum SubscribeErrorCode: String, CaseIterable, Codable {
|
||||
case alreadySubscribed = "ALREADY_SUBSCRIBED"
|
||||
|
||||
case badRequest = "BAD_REQUEST"
|
||||
|
||||
case notFound = "NOT_FOUND"
|
||||
|
||||
case unauthorized = "UNAUTHORIZED"
|
||||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// SubscriptionsErrorCode
|
||||
enum SubscriptionsErrorCode: String, CaseIterable, Codable {
|
||||
|
|
@ -19649,6 +20385,21 @@ extension Enums {
|
|||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// UpdatePageErrorCode
|
||||
enum UpdatePageErrorCode: String, CaseIterable, Codable {
|
||||
case badRequest = "BAD_REQUEST"
|
||||
|
||||
case forbidden = "FORBIDDEN"
|
||||
|
||||
case notFound = "NOT_FOUND"
|
||||
|
||||
case unauthorized = "UNAUTHORIZED"
|
||||
|
||||
case updateFailed = "UPDATE_FAILED"
|
||||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// UpdateReminderErrorCode
|
||||
enum UpdateReminderErrorCode: String, CaseIterable, Codable {
|
||||
|
|
@ -20604,6 +21355,29 @@ extension InputObjects {
|
|||
}
|
||||
}
|
||||
|
||||
extension InputObjects {
|
||||
struct UpdatePageInput: Encodable, Hashable {
|
||||
var description: OptionalArgument<String> = .absent()
|
||||
|
||||
var pageId: String
|
||||
|
||||
var title: OptionalArgument<String> = .absent()
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
if description.hasValue { try container.encode(description, forKey: .description) }
|
||||
try container.encode(pageId, forKey: .pageId)
|
||||
if title.hasValue { try container.encode(title, forKey: .title) }
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case description
|
||||
case pageId
|
||||
case title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension InputObjects {
|
||||
struct UpdateReminderInput: Encodable, Hashable {
|
||||
var archiveUntil: Bool
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import CoreData
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
public extension DataService {
|
||||
func deleteSubscription(subscriptionName: String) async throws {
|
||||
enum MutationResult {
|
||||
case success(id: String)
|
||||
case error(errorMessage: String)
|
||||
}
|
||||
|
||||
let selection = Selection<MutationResult, Unions.UnsubscribeResult> {
|
||||
try $0.on(
|
||||
unsubscribeError: .init { .error(errorMessage: (try $0.errorCodes().first ?? .unauthorized).rawValue) },
|
||||
unsubscribeSuccess: .init { .success(id: try $0.subscription(selection: Selection.Subscription { try $0.id() })) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.unsubscribe(name: subscriptionName, selection: selection)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
let headers = networker.defaultHeaders
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(mutation, to: path, headers: headers) { mutationResult in
|
||||
guard let payload = try? mutationResult.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network request failed"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case .success:
|
||||
continuation.resume()
|
||||
case .error:
|
||||
continuation.resume(throwing: BasicError.message(messageText: "Subscriptions fetch error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,16 +22,16 @@ public struct SolidCapsuleButtonStyle: ButtonStyle {
|
|||
}
|
||||
}
|
||||
|
||||
struct RoundedRectButtonStyle: ButtonStyle {
|
||||
public struct RoundedRectButtonStyle: ButtonStyle {
|
||||
let backgroundColor: Color
|
||||
let textColor: Color
|
||||
|
||||
init(color: Color = .appButtonBackground, textColor: Color = .appGrayText) {
|
||||
public init(color: Color = .appButtonBackground, textColor: Color = .appGrayText) {
|
||||
self.backgroundColor = color
|
||||
self.textColor = textColor
|
||||
}
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.font(.appBody)
|
||||
.foregroundColor(textColor)
|
||||
|
|
|
|||
Loading…
Reference in a new issue