mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Add feed buttons for iOS
This commit is contained in:
parent
f47c83d3eb
commit
6764f56cdd
14 changed files with 448 additions and 233 deletions
|
|
@ -33,7 +33,6 @@ struct FeedCardNavigationLink: View {
|
|||
@EnvironmentObject var audioController: AudioController
|
||||
|
||||
let item: Models.LibraryItem
|
||||
let isInMultiSelectMode: Bool
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
|
||||
var body: some View {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
struct HomeFeedContainerView: View {
|
||||
@State var hasHighlightMutations = false
|
||||
@State var searchPresented = false
|
||||
@State var addLinkPresented = false
|
||||
@State var showAddLinkView = false
|
||||
@State var showAddFeedView = false
|
||||
@State var isListScrolled = false
|
||||
@State var listTitle = ""
|
||||
@State var isEditMode: EditMode = .inactive
|
||||
|
|
@ -70,6 +71,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
isListScrolled: $isListScrolled,
|
||||
prefersListLayout: $prefersListLayout,
|
||||
isEditMode: $isEditMode,
|
||||
showAddFeedView: $showAddFeedView,
|
||||
selection: $selection,
|
||||
viewModel: viewModel,
|
||||
showFeatureCards: showFeatureCards
|
||||
|
|
@ -174,7 +176,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
.fullScreenCover(isPresented: $searchPresented) {
|
||||
LibrarySearchView(homeFeedViewModel: self.viewModel)
|
||||
}
|
||||
.sheet(isPresented: $addLinkPresented) {
|
||||
.sheet(isPresented: $showAddLinkView) {
|
||||
NavigationView {
|
||||
LibraryAddLinkView()
|
||||
}
|
||||
|
|
@ -188,6 +190,54 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
.environment(\.editMode, self.$isEditMode)
|
||||
}
|
||||
|
||||
var trailingItems: some ToolbarContent {
|
||||
Group {
|
||||
ToolbarItem(placement: UIDevice.isIPhone ? .barLeading : .barTrailing) {
|
||||
if enableGrid {
|
||||
Button(
|
||||
action: { prefersListLayout.toggle() },
|
||||
label: {
|
||||
Label("Toggle Feed Layout", systemImage: prefersListLayout ? "square.grid.2x2" : "list.bullet")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: {
|
||||
if viewModel.folder == "inbox" {
|
||||
showAddLinkView = true
|
||||
} else if viewModel.folder == "following" {
|
||||
showAddFeedView = true
|
||||
}
|
||||
},
|
||||
label: {
|
||||
Image.addLink
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: { isEditMode = isEditMode == .active ? .inactive : .active },
|
||||
label: {
|
||||
Image.selectMultiple
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: { searchPresented = true },
|
||||
label: {
|
||||
Image.magnifyingGlass
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var toolbarItems: some ToolbarContent {
|
||||
Group {
|
||||
ToolbarItem(placement: .barLeading) {
|
||||
|
|
@ -205,47 +255,9 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
}
|
||||
.frame(maxWidth: .infinity, alignment: .bottomLeading)
|
||||
}
|
||||
ToolbarItem(placement: UIDevice.isIPhone ? .barLeading : .barTrailing) {
|
||||
if enableGrid {
|
||||
Button(
|
||||
action: { prefersListLayout.toggle() },
|
||||
label: {
|
||||
Label("Toggle Feed Layout", systemImage: prefersListLayout ? "square.grid.2x2" : "list.bullet")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: { searchPresented = true },
|
||||
label: {
|
||||
Image.magnifyingGlass
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: { isEditMode = isEditMode == .active ? .inactive : .active },
|
||||
label: {
|
||||
Image.selectMultiple
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
if viewModel.folder == "inbox" {
|
||||
Button(
|
||||
action: { addLinkPresented = true },
|
||||
label: {
|
||||
Image.addLink
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
trailingItems
|
||||
|
||||
ToolbarItemGroup(placement: .bottomBar) {
|
||||
if isEditMode == .active {
|
||||
Button(action: {
|
||||
|
|
@ -274,6 +286,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
@Binding var isListScrolled: Bool
|
||||
@Binding var prefersListLayout: Bool
|
||||
@Binding var isEditMode: EditMode
|
||||
@Binding var showAddFeedView: Bool
|
||||
@Binding var selection: Set<String>
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
|
||||
|
|
@ -304,6 +317,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
isListScrolled: $isListScrolled,
|
||||
prefersListLayout: $prefersListLayout,
|
||||
isEditMode: $isEditMode,
|
||||
showAddFeedView: $showAddFeedView,
|
||||
selection: $selection,
|
||||
viewModel: viewModel,
|
||||
showFeatureCards: showFeatureCards
|
||||
|
|
@ -357,7 +371,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
@Binding var isListScrolled: Bool
|
||||
@Binding var prefersListLayout: Bool
|
||||
@Binding var isEditMode: EditMode
|
||||
@State private var showAddFeedView = false
|
||||
@Binding var showAddFeedView: Bool
|
||||
@State private var showHideFeatureAlert = false
|
||||
@State private var showHideFollowingAlert = false
|
||||
|
||||
|
|
@ -632,7 +646,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
|
||||
FeedCardNavigationLink(
|
||||
item: item,
|
||||
isInMultiSelectMode: viewModel.isInMultiSelectMode,
|
||||
viewModel: viewModel
|
||||
)
|
||||
.background(GeometryReader { geometry in
|
||||
|
|
@ -735,7 +748,9 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
}
|
||||
.sheet(isPresented: $showAddFeedView) {
|
||||
NavigationView {
|
||||
LibraryAddFeedView()
|
||||
LibraryAddFeedView(dismiss: {
|
||||
showAddFeedView = false
|
||||
})
|
||||
}
|
||||
}
|
||||
.alert("The Feature Section will be removed from your library. You can add it back from the filter settings in your profile.",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import Views
|
|||
@Published var linkRequest: LinkRequest?
|
||||
@Published var presentWebContainer = false
|
||||
@Published var showLoadingBar = false
|
||||
@Published var isInMultiSelectMode = false
|
||||
|
||||
@Published var selectedLinkItem: NSManagedObjectID? // used by mac app only
|
||||
@Published var selectedItem: Models.LibraryItem?
|
||||
|
|
|
|||
|
|
@ -5,43 +5,10 @@ import Services
|
|||
import SwiftUI
|
||||
import Views
|
||||
|
||||
@MainActor final class LibraryAddFeedViewModel: NSObject, ObservableObject {
|
||||
@Published var isLoading = false
|
||||
@Published var errorMessage: String = ""
|
||||
@Published var showErrorMessage: Bool = false
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
func addLink(dataService: DataService, newLinkURL: String, dismiss: DismissAction) {
|
||||
isLoading = true
|
||||
Task {
|
||||
if URL(string: newLinkURL) == nil {
|
||||
error("Invalid link")
|
||||
} else {
|
||||
let result = try? await dataService.saveURL(id: UUID().uuidString, url: newLinkURL)
|
||||
if result == nil {
|
||||
error("Error adding link")
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
func error(_ msg: String) {
|
||||
errorMessage = msg
|
||||
showErrorMessage = true
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
struct LibraryAddFeedView: View {
|
||||
@StateObject var viewModel = LibraryAddFeedViewModel()
|
||||
|
||||
let dismiss: () -> Void
|
||||
@State var newLinkURL: String = ""
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
enum FocusField: Hashable {
|
||||
case addLinkEditor
|
||||
|
|
@ -54,7 +21,7 @@ struct LibraryAddFeedView: View {
|
|||
#if os(iOS)
|
||||
Form {
|
||||
innerBody
|
||||
.navigationTitle("Add Link")
|
||||
.navigationTitle("Add Feed URL")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
#else
|
||||
|
|
@ -67,7 +34,6 @@ struct LibraryAddFeedView: View {
|
|||
.onAppear {
|
||||
focusedField = .addLinkEditor
|
||||
}
|
||||
.navigationTitle("Add Link")
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
|
|
@ -75,14 +41,13 @@ struct LibraryAddFeedView: View {
|
|||
dismissButton
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
viewModel.isLoading ? AnyView(ProgressView()) : AnyView(addButton)
|
||||
NavigationLink(
|
||||
destination: LibraryScanFeedView(dismiss: self.dismiss, viewModel: LibraryAddFeedViewModel(dataService: dataService, feedURL: newLinkURL)),
|
||||
label: { Text("Add").bold() }
|
||||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alert(viewModel.errorMessage,
|
||||
isPresented: $viewModel.showErrorMessage) {
|
||||
Button(LocalText.genericOk, role: .cancel) { viewModel.showErrorMessage = false }
|
||||
}
|
||||
}
|
||||
|
||||
var cancelButton: some View {
|
||||
|
|
@ -102,55 +67,30 @@ struct LibraryAddFeedView: View {
|
|||
|
||||
var innerBody: some View {
|
||||
Group {
|
||||
TextField("Add Link", text: $newLinkURL)
|
||||
TextField("Feed or site URL", text: $newLinkURL)
|
||||
#if os(iOS)
|
||||
.keyboardType(.URL)
|
||||
#endif
|
||||
.autocorrectionDisabled(true)
|
||||
.textFieldStyle(StandardTextFieldStyle())
|
||||
.focused($focusedField, equals: .addLinkEditor)
|
||||
// .focused($focusedField, equals: .addLinkEditor)
|
||||
|
||||
Button(action: {
|
||||
if let url = pasteboardString {
|
||||
newLinkURL = url
|
||||
} else {
|
||||
viewModel.error("No URL on pasteboard")
|
||||
// viewModel.error("No URL on pasteboard")
|
||||
}
|
||||
}, label: {
|
||||
Text("Get from pasteboard")
|
||||
})
|
||||
|
||||
#if os(macOS)
|
||||
Spacer()
|
||||
HStack {
|
||||
cancelButton
|
||||
Spacer()
|
||||
addButton
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var addButton: some View {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss)
|
||||
},
|
||||
label: { Text("Add").bold() }
|
||||
)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
.onSubmit {
|
||||
viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss)
|
||||
}
|
||||
.disabled(viewModel.isLoading)
|
||||
}
|
||||
|
||||
var dismissButton: some View {
|
||||
Button(
|
||||
action: { dismiss() },
|
||||
label: { Text(LocalText.genericClose) }
|
||||
)
|
||||
.disabled(viewModel.isLoading)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
import Foundation
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Utils
|
||||
|
||||
@MainActor
|
||||
public class LibraryAddFeedViewModel: NSObject, ObservableObject {
|
||||
let dataService: DataService
|
||||
let feedURL: String
|
||||
|
||||
@Published var isLoading = true
|
||||
@Published var errorMessage: String = ""
|
||||
@Published var showErrorMessage: Bool = false
|
||||
|
||||
@Published var feeds: [Feed] = []
|
||||
@Published var selected: [String] = []
|
||||
|
||||
init(dataService: DataService, feedURL: String) {
|
||||
self.dataService = dataService
|
||||
self.feedURL = feedURL
|
||||
}
|
||||
|
||||
func scanFeed() async {
|
||||
isLoading = true
|
||||
if let feedURL = URL(string: feedURL) {
|
||||
let result = try? await dataService.scanFeed(feedURL: feedURL)
|
||||
if let feeds = result {
|
||||
self.feeds = feeds
|
||||
selected = feeds.map(\.url)
|
||||
} else {
|
||||
feeds = []
|
||||
error("Error adding feed")
|
||||
}
|
||||
} else {
|
||||
error("invalid URL")
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
func addFeeds() async {
|
||||
_ = await withTaskGroup(of: Bool.self) { group in
|
||||
for feedURL in selected {
|
||||
group.addTask {
|
||||
(try? await self.dataService.subscribeToFeed(feedURL: feedURL)) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
var successCount = 0
|
||||
var failureCount = 0
|
||||
for await value in group {
|
||||
if value {
|
||||
successCount += 1
|
||||
} else {
|
||||
failureCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(4000)) {
|
||||
if failureCount > 0 {
|
||||
showInLibrarySnackbar("Failed to add \(failureCount) feeds")
|
||||
} else {
|
||||
showInLibrarySnackbar("Added \(successCount) feed\(successCount == 0 ? "" : "s")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func error(_ msg: String) {
|
||||
errorMessage = msg
|
||||
showErrorMessage = true
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public struct LibraryScanFeedView: View {
|
||||
let dismiss: () -> Void
|
||||
@StateObject var viewModel: LibraryAddFeedViewModel
|
||||
|
||||
func isSelected(_ url: String) -> Bool {
|
||||
viewModel.selected.contains(url)
|
||||
}
|
||||
|
||||
var innerBody: some View {
|
||||
if viewModel.isLoading {
|
||||
AnyView(ProgressView().frame(maxWidth: .infinity, alignment: .center))
|
||||
} else if viewModel.feeds.count == 0 {
|
||||
AnyView(Text("No feeds found for URL"))
|
||||
} else {
|
||||
AnyView(List {
|
||||
Section("Choose the feeds to add") {
|
||||
ForEach(viewModel.feeds, id: \.title) { feed in
|
||||
Button(action: {
|
||||
if !isSelected(feed.url) {
|
||||
viewModel.selected.append(feed.url)
|
||||
} else {
|
||||
if let idx = viewModel.selected.firstIndex(of: feed.url) {
|
||||
viewModel.selected.remove(at: idx)
|
||||
}
|
||||
}
|
||||
}, label: {
|
||||
HStack {
|
||||
Text(feed.title)
|
||||
Spacer()
|
||||
if isSelected(feed.url) {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
Form {
|
||||
innerBody
|
||||
.navigationTitle("Select Feeds")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}.task {
|
||||
await viewModel.scanFeed()
|
||||
}
|
||||
#else
|
||||
innerBody
|
||||
#endif
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
showInLibrarySnackbar("Adding feeds...")
|
||||
Task {
|
||||
await viewModel.addFeeds()
|
||||
}
|
||||
}, label: {
|
||||
Text("Done").bold()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6655,6 +6655,7 @@ extension Selection where TypeLock == Never, Type == Never {
|
|||
extension Objects {
|
||||
struct Filter {
|
||||
let __typename: TypeName = .filter
|
||||
let category: [String: String]
|
||||
let createdAt: [String: DateTime]
|
||||
let defaultFilter: [String: Bool]
|
||||
let description: [String: String]
|
||||
|
|
@ -6684,6 +6685,10 @@ extension Objects.Filter: Decodable {
|
|||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "category":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "createdAt":
|
||||
if let value = try container.decode(DateTime?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
|
|
@ -6734,6 +6739,7 @@ extension Objects.Filter: Decodable {
|
|||
}
|
||||
}
|
||||
|
||||
category = map["category"]
|
||||
createdAt = map["createdAt"]
|
||||
defaultFilter = map["defaultFilter"]
|
||||
description = map["description"]
|
||||
|
|
@ -6748,6 +6754,21 @@ extension Objects.Filter: Decodable {
|
|||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.Filter {
|
||||
func category() throws -> String? {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "category",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
return data.category[field.alias!]
|
||||
case .mocking:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func createdAt() throws -> DateTime {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "createdAt",
|
||||
|
|
@ -6814,7 +6835,7 @@ extension Fields where TypeLock == Objects.Filter {
|
|||
}
|
||||
}
|
||||
|
||||
func folder() throws -> String {
|
||||
func folder() throws -> String? {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "folder",
|
||||
arguments: []
|
||||
|
|
@ -6823,12 +6844,9 @@ extension Fields where TypeLock == Objects.Filter {
|
|||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
if let data = data.folder[field.alias!] {
|
||||
return data
|
||||
}
|
||||
throw HttpError.badpayload
|
||||
return data.folder[field.alias!]
|
||||
case .mocking:
|
||||
return String.mockValue
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -11469,10 +11487,10 @@ extension Fields where TypeLock == Objects.Mutation {
|
|||
}
|
||||
}
|
||||
|
||||
func bulkAction<Type>(action: Enums.BulkActionType, async: OptionalArgument<Bool> = .absent(), expectedCount: OptionalArgument<Int> = .absent(), labelIds: OptionalArgument<[String]> = .absent(), query: String, selection: Selection<Type, Unions.BulkActionResult>) throws -> Type {
|
||||
func bulkAction<Type>(action: Enums.BulkActionType, arguments: OptionalArgument<String> = .absent(), async: OptionalArgument<Bool> = .absent(), expectedCount: OptionalArgument<Int> = .absent(), labelIds: OptionalArgument<[String]> = .absent(), query: String, selection: Selection<Type, Unions.BulkActionResult>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "bulkAction",
|
||||
arguments: [Argument(name: "action", type: "BulkActionType!", value: action), Argument(name: "async", type: "Boolean", value: async), Argument(name: "expectedCount", type: "Int", value: expectedCount), Argument(name: "labelIds", type: "[ID!]", value: labelIds), Argument(name: "query", type: "String!", value: query)],
|
||||
arguments: [Argument(name: "action", type: "BulkActionType!", value: action), Argument(name: "arguments", type: "JSON", value: arguments), Argument(name: "async", type: "Boolean", value: async), Argument(name: "expectedCount", type: "Int", value: expectedCount), Argument(name: "labelIds", type: "[ID!]", value: labelIds), Argument(name: "query", type: "String!", value: query)],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
|
@ -32984,6 +33002,8 @@ extension Enums {
|
|||
case delete = "DELETE"
|
||||
|
||||
case markAsRead = "MARK_AS_READ"
|
||||
|
||||
case moveToFolder = "MOVE_TO_FOLDER"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33682,15 +33702,6 @@ extension Enums {
|
|||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// ScanFeedsType
|
||||
enum ScanFeedsType: String, CaseIterable, Codable {
|
||||
case html = "HTML"
|
||||
|
||||
case opml = "OPML"
|
||||
}
|
||||
}
|
||||
|
||||
extension Enums {
|
||||
/// SearchErrorCode
|
||||
enum SearchErrorCode: String, CaseIterable, Codable {
|
||||
|
|
@ -34260,6 +34271,12 @@ extension InputObjects {
|
|||
|
||||
var preparedDocument: OptionalArgument<InputObjects.PreparedDocumentInput> = .absent()
|
||||
|
||||
var publishedAt: OptionalArgument<DateTime> = .absent()
|
||||
|
||||
var rssFeedUrl: OptionalArgument<String> = .absent()
|
||||
|
||||
var savedAt: OptionalArgument<DateTime> = .absent()
|
||||
|
||||
var skipParsing: OptionalArgument<Bool> = .absent()
|
||||
|
||||
var source: OptionalArgument<String> = .absent()
|
||||
|
|
@ -34276,6 +34293,9 @@ extension InputObjects {
|
|||
if folder.hasValue { try container.encode(folder, forKey: .folder) }
|
||||
if labels.hasValue { try container.encode(labels, forKey: .labels) }
|
||||
if preparedDocument.hasValue { try container.encode(preparedDocument, forKey: .preparedDocument) }
|
||||
if publishedAt.hasValue { try container.encode(publishedAt, forKey: .publishedAt) }
|
||||
if rssFeedUrl.hasValue { try container.encode(rssFeedUrl, forKey: .rssFeedUrl) }
|
||||
if savedAt.hasValue { try container.encode(savedAt, forKey: .savedAt) }
|
||||
if skipParsing.hasValue { try container.encode(skipParsing, forKey: .skipParsing) }
|
||||
if source.hasValue { try container.encode(source, forKey: .source) }
|
||||
if state.hasValue { try container.encode(state, forKey: .state) }
|
||||
|
|
@ -34288,6 +34308,9 @@ extension InputObjects {
|
|||
case folder
|
||||
case labels
|
||||
case preparedDocument
|
||||
case publishedAt
|
||||
case rssFeedUrl
|
||||
case savedAt
|
||||
case skipParsing
|
||||
case source
|
||||
case state
|
||||
|
|
@ -35031,6 +35054,8 @@ extension InputObjects {
|
|||
|
||||
extension InputObjects {
|
||||
struct SaveFilterInput: Encodable, Hashable {
|
||||
var category: OptionalArgument<String> = .absent()
|
||||
|
||||
var description: OptionalArgument<String> = .absent()
|
||||
|
||||
var filter: String
|
||||
|
|
@ -35043,6 +35068,7 @@ extension InputObjects {
|
|||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
if category.hasValue { try container.encode(category, forKey: .category) }
|
||||
if description.hasValue { try container.encode(description, forKey: .description) }
|
||||
try container.encode(filter, forKey: .filter)
|
||||
if folder.hasValue { try container.encode(folder, forKey: .folder) }
|
||||
|
|
@ -35051,6 +35077,7 @@ extension InputObjects {
|
|||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case category
|
||||
case description
|
||||
case filter
|
||||
case folder
|
||||
|
|
@ -35174,20 +35201,16 @@ extension InputObjects {
|
|||
struct ScanFeedsInput: Encodable, Hashable {
|
||||
var opml: OptionalArgument<String> = .absent()
|
||||
|
||||
var type: Enums.ScanFeedsType
|
||||
|
||||
var url: OptionalArgument<String> = .absent()
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
if opml.hasValue { try container.encode(opml, forKey: .opml) }
|
||||
try container.encode(type, forKey: .type)
|
||||
if url.hasValue { try container.encode(url, forKey: .url) }
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case opml
|
||||
case type
|
||||
case url
|
||||
}
|
||||
}
|
||||
|
|
@ -35577,6 +35600,8 @@ extension InputObjects {
|
|||
|
||||
extension InputObjects {
|
||||
struct UpdateFilterInput: Encodable, Hashable {
|
||||
var category: OptionalArgument<String> = .absent()
|
||||
|
||||
var description: OptionalArgument<String> = .absent()
|
||||
|
||||
var filter: OptionalArgument<String> = .absent()
|
||||
|
|
@ -35593,6 +35618,7 @@ extension InputObjects {
|
|||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
if category.hasValue { try container.encode(category, forKey: .category) }
|
||||
if description.hasValue { try container.encode(description, forKey: .description) }
|
||||
if filter.hasValue { try container.encode(filter, forKey: .filter) }
|
||||
if folder.hasValue { try container.encode(folder, forKey: .folder) }
|
||||
|
|
@ -35603,6 +35629,7 @@ extension InputObjects {
|
|||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case category
|
||||
case description
|
||||
case filter
|
||||
case folder
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
public extension DataService {
|
||||
func subscribeToFeed(feedURL: String) async throws -> Bool {
|
||||
enum MutationResult {
|
||||
case success(subscriptions: [Subscription])
|
||||
case error(errorMessage: String)
|
||||
}
|
||||
|
||||
let selection = Selection<MutationResult, Unions.SubscribeResult> {
|
||||
try $0.on(
|
||||
subscribeError: .init {
|
||||
.error(errorMessage: try $0.errorCodes().first?.rawValue ?? "unknown error")
|
||||
},
|
||||
subscribeSuccess: .init { .success(subscriptions: try $0.subscriptions(selection: subscriptionSelection.list)) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.subscribe(input: InputObjects.SubscribeInput(url: feedURL), 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: "failed to add feed \(feedURL)"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case .success:
|
||||
print("subscribed to feed:", feedURL)
|
||||
continuation.resume(returning: true)
|
||||
case .error:
|
||||
print("failed to subscribe to feed:", feedURL)
|
||||
continuation.resume(throwing: BasicError.message(messageText: "failed to add feed \(feedURL)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import CoreData
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
public struct Feed {
|
||||
public var title: String
|
||||
public var url: String
|
||||
public var description: String?
|
||||
public var image: String?
|
||||
public var author: String?
|
||||
}
|
||||
|
||||
let feedSelection = Selection.Feed {
|
||||
Feed(
|
||||
title: try $0.title(),
|
||||
url: try $0.url(),
|
||||
description: try $0.description(),
|
||||
image: try $0.image(),
|
||||
author: try $0.author()
|
||||
)
|
||||
}
|
||||
|
||||
public extension DataService {
|
||||
func scanFeed(feedURL: URL) async throws -> [Feed] {
|
||||
enum QueryResult {
|
||||
case success(result: [Feed])
|
||||
case error(error: String)
|
||||
}
|
||||
|
||||
let selection = Selection<QueryResult, Unions.ScanFeedsResult> {
|
||||
try $0.on(
|
||||
scanFeedsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
scanFeedsSuccess: .init {
|
||||
QueryResult.success(result: try $0.feeds(selection: feedSelection.list))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
try $0.scanFeeds(
|
||||
input: InputObjects.ScanFeedsInput(url: OptionalArgument(feedURL.absoluteString)),
|
||||
selection: selection
|
||||
)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
let headers = networker.defaultHeaders
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(query, to: path, headers: headers) { queryResult in
|
||||
print("QUERY RESULT: ", queryResult)
|
||||
guard let payload = try? queryResult.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network request failed"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case let .success(result: feeds):
|
||||
continuation.resume(returning: feeds)
|
||||
case .error:
|
||||
continuation.resume(throwing: BasicError.message(messageText: "scanFeed fetch error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,29 +9,13 @@ public extension DataService {
|
|||
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 ?? Date(),
|
||||
url: try $0.url(),
|
||||
icon: try $0.icon()
|
||||
)
|
||||
}
|
||||
|
||||
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))
|
||||
QueryResult.success(result: try $0.subscriptions(selection: subscriptionSelection.list))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -60,16 +44,3 @@ public extension DataService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SubscriptionStatus {
|
||||
static func make(from status: Enums.SubscriptionStatus) -> SubscriptionStatus {
|
||||
switch status {
|
||||
case .active:
|
||||
return .active
|
||||
case .deleted:
|
||||
return .deleted
|
||||
case .unsubscribed:
|
||||
return .unsubscribed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ let filterSelection = Selection.Filter {
|
|||
InternalFilter(
|
||||
id: try $0.id(),
|
||||
name: try $0.name(),
|
||||
folder: try $0.folder(),
|
||||
folder: try $0.folder() ?? "inbox",
|
||||
filter: try $0.filter(),
|
||||
visible: try $0.visible() ?? true,
|
||||
position: try $0.position(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
let subscriptionSelection = 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 ?? Date(),
|
||||
url: try $0.url(),
|
||||
icon: try $0.icon()
|
||||
)
|
||||
}
|
||||
|
||||
extension SubscriptionStatus {
|
||||
static func make(from status: Enums.SubscriptionStatus) -> SubscriptionStatus {
|
||||
switch status {
|
||||
case .active:
|
||||
return .active
|
||||
case .deleted:
|
||||
return .deleted
|
||||
case .unsubscribed:
|
||||
return .unsubscribed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
"@opentelemetry/tracing": "^0.24.0",
|
||||
"@sendgrid/mail": "^7.6.0",
|
||||
"@sentry/integrations": "^7.10.0",
|
||||
"@sentry/node": "^5.26.0",
|
||||
"@sentry/node": "^7.9.0",
|
||||
"@sentry/tracing": "^7.9.0",
|
||||
"addressparser": "^1.0.1",
|
||||
"analytics-node": "^6.0.0",
|
||||
|
|
|
|||
96
yarn.lock
96
yarn.lock
|
|
@ -5858,6 +5858,15 @@
|
|||
"@sentry/types" "7.77.0"
|
||||
"@sentry/utils" "7.77.0"
|
||||
|
||||
"@sentry-internal/tracing@7.86.0":
|
||||
version "7.86.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.86.0.tgz#657e80eb7d08d1030393902c1a7bc47fc39ccb2d"
|
||||
integrity sha512-b4dUsNWlPWRwakGwR7bhOkqiFlqQszH1hhVFwrm/8s3kqEBZ+E4CeIfCvuHBHQ1cM/fx55xpXX/BU163cy+3iQ==
|
||||
dependencies:
|
||||
"@sentry/core" "7.86.0"
|
||||
"@sentry/types" "7.86.0"
|
||||
"@sentry/utils" "7.86.0"
|
||||
|
||||
"@sentry/browser@7.50.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.50.0.tgz#16c995c336322c8aec65570f90f50288678004ec"
|
||||
|
|
@ -5882,17 +5891,6 @@
|
|||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@sentry/core@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
|
||||
integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/minimal" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@7.50.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.50.0.tgz#88bc9cbfc0cb429a28489ece6f0be7a7006436c4"
|
||||
|
|
@ -5910,14 +5908,13 @@
|
|||
"@sentry/types" "7.77.0"
|
||||
"@sentry/utils" "7.77.0"
|
||||
|
||||
"@sentry/hub@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
|
||||
integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==
|
||||
"@sentry/core@7.86.0":
|
||||
version "7.86.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.86.0.tgz#d01f538783dee9a0d79141a63145392ad2c1cb89"
|
||||
integrity sha512-SbLvqd1bRYzhDS42u7GMnmbDMfth/zRiLElQWbLK/shmuZzTcfQSwNNdF4Yj+VfjOkqPFgGmICHSHVUc9dh01g==
|
||||
dependencies:
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/types" "7.86.0"
|
||||
"@sentry/utils" "7.86.0"
|
||||
|
||||
"@sentry/integrations@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -5939,15 +5936,6 @@
|
|||
"@sentry/utils" "7.77.0"
|
||||
localforage "^1.8.1"
|
||||
|
||||
"@sentry/minimal@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b"
|
||||
integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/nextjs@^7.42.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.50.0.tgz#b9e7727c8f974644bb84a01f40a0adedb44ec416"
|
||||
|
|
@ -5991,20 +5979,16 @@
|
|||
"@sentry/utils" "7.77.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
|
||||
"@sentry/node@^5.26.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48"
|
||||
integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==
|
||||
"@sentry/node@^7.9.0":
|
||||
version "7.86.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.86.0.tgz#416db178aeb64f7895a23ae1c3d4d65ce51ed50a"
|
||||
integrity sha512-cB1bn/LMn2Km97Y3hv63xwWxT50/G5ixGuSxTZ3dCQM6VDhmZoCuC5NGT3itVvaRd6upQXRZa5W0Zgyh0HXKig==
|
||||
dependencies:
|
||||
"@sentry/core" "5.30.0"
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/tracing" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
cookie "^0.4.1"
|
||||
"@sentry-internal/tracing" "7.86.0"
|
||||
"@sentry/core" "7.86.0"
|
||||
"@sentry/types" "7.86.0"
|
||||
"@sentry/utils" "7.86.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -6038,17 +6022,6 @@
|
|||
"@types/aws-lambda" "^8.10.62"
|
||||
"@types/express" "^4.17.14"
|
||||
|
||||
"@sentry/tracing@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f"
|
||||
integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/minimal" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@^7.9.0":
|
||||
version "7.77.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.77.0.tgz#39d7c30834f503fe9eb20ce1c8c8bd28f7d7c9ce"
|
||||
|
|
@ -6056,11 +6029,6 @@
|
|||
dependencies:
|
||||
"@sentry-internal/tracing" "7.77.0"
|
||||
|
||||
"@sentry/types@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
|
||||
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
|
||||
|
||||
"@sentry/types@7.50.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.50.0.tgz#52a035cad83a80ca26fa53c09eb1241250c3df3e"
|
||||
|
|
@ -6071,13 +6039,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.77.0.tgz#c5d00fe547b89ccde59cdea59143bf145cee3144"
|
||||
integrity sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==
|
||||
|
||||
"@sentry/utils@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
|
||||
integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==
|
||||
dependencies:
|
||||
"@sentry/types" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/types@7.86.0":
|
||||
version "7.86.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.86.0.tgz#56ed2f5b15e8130ea5ecfbbc4102d88eaa3b3a67"
|
||||
integrity sha512-pGAt0+bMfWgo0KG2epthfNV4Wae03tURpoxNjGo5Fr4cXxvLTSijSAQ6rmmO4bXBJ7+rErEjX30g30o/eEdP9g==
|
||||
|
||||
"@sentry/utils@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -6094,6 +6059,13 @@
|
|||
dependencies:
|
||||
"@sentry/types" "7.77.0"
|
||||
|
||||
"@sentry/utils@7.86.0":
|
||||
version "7.86.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.86.0.tgz#356ec19bf1e3e5c40935dd987fd15bee8c6b37ba"
|
||||
integrity sha512-6PejFtw9VTFFy5vu0ks+U7Ozkqz+eMt+HN8AZKBKErYzX5/xs0kpkOcSRpu3ETdTYcZf8VAmLVgFgE2BE+3WuQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.86.0"
|
||||
|
||||
"@sentry/webpack-plugin@1.20.0":
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
|
||||
|
|
|
|||
Loading…
Reference in a new issue