diff --git a/.gitignore b/.gitignore index 5d35aee6..be42ae07 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ fastlane/metadata/trade_representative_contact_information vendor/ .bundle/ + +Config.xcconfig \ No newline at end of file diff --git a/.swift-version b/.swift-version index 3659ea2f..95ee81a4 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.8 +5.9 diff --git a/App/Configuration.storekit b/App/Configuration.storekit deleted file mode 100644 index 089c7f6c..00000000 --- a/App/Configuration.storekit +++ /dev/null @@ -1,33 +0,0 @@ -{ - "identifier" : "98370847", - "nonRenewingSubscriptions" : [ - - ], - "products" : [ - { - "displayPrice" : "0.99", - "familyShareable" : false, - "internalID" : "FE8C892D", - "localizations" : [ - { - "description" : "tips1", - "displayName" : "tips1", - "locale" : "en_US" - } - ], - "productID" : "tips1", - "referenceName" : "tips1", - "type" : "Consumable" - } - ], - "settings" : { - - }, - "subscriptionGroups" : [ - - ], - "version" : { - "major" : 1, - "minor" : 1 - } -} diff --git a/App/Entity/Entry.swift b/App/Entity/Entry.swift index bc0cf6fa..ff7756e7 100644 --- a/App/Entity/Entry.swift +++ b/App/Entity/Entry.swift @@ -2,6 +2,7 @@ import CoreData import CoreSpotlight import Foundation import SharedLib + // import MobileCoreServices import WallabagKit diff --git a/App/Features/AI/ChatAssistant.swift b/App/Features/AI/ChatAssistant.swift new file mode 100644 index 00000000..5d533bd2 --- /dev/null +++ b/App/Features/AI/ChatAssistant.swift @@ -0,0 +1,56 @@ +import Foundation +import HTTPTypes +import OpenAPIRuntime +import OpenAPIURLSession + +protocol ChatAssistantProtocol { + func generateSynthesis(content: String) async throws -> String + func generateTags(content: String) async throws -> [String] +} + +struct ChatAssistant: ChatAssistantProtocol { + var client: Client { + get throws { + try Client( + serverURL: Servers.server2(), + transport: URLSessionTransport(), + middlewares: [AuthenticationMiddleware()] + ) + } + } + + var locale = Locale.current.identifier + + func generateSynthesis(content: String) async throws -> String { + let response = try await client.wallabagSynthesis(body: .json(.init(body: content, language: locale))) + + return try response.ok.body.json.content ?? "" + } + + func generateTags(content: String) async throws -> [String] { + let response = try await client.wallabagTags(body: .json(.init(body: content, language: locale))) + + return try response.ok.body.json.tags ?? [] + } +} + +private struct AuthenticationMiddleware: ClientMiddleware { + @BundleKey("GPTBACK_KEY") + private var gptBackKey + + func intercept( + _ request: HTTPTypes.HTTPRequest, + body: OpenAPIRuntime.HTTPBody?, + baseURL: URL, + operationID _: String, + next: @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, URL) async throws -> ( + HTTPTypes.HTTPResponse, + OpenAPIRuntime.HTTPBody? + ) + ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { + var request = request + request.headerFields[.authorization] = "Bearer \(gptBackKey)" + + return try await next(request, body, baseURL) + } +} diff --git a/App/Features/AI/Synthesis/SynthesisEntryView.swift b/App/Features/AI/Synthesis/SynthesisEntryView.swift new file mode 100644 index 00000000..64c2c82d --- /dev/null +++ b/App/Features/AI/Synthesis/SynthesisEntryView.swift @@ -0,0 +1,39 @@ +// +// SynthesisEntryView.swift +// wallabag +// +// Created by maxime marinel on 11/12/2023. +// + +import Factory +import SwiftUI + +struct SynthesisEntryView: View { + @StateObject private var viewModel = SynthesisEntryViewModel() + let entry: Entry + + var body: some View { + ScrollView { + if viewModel.isLoading { + Text("Your assistant is working") + ProgressView() + } else { + Text(viewModel.synthesis) + .padding() + .fontDesign(.serif) + } + } + .navigationTitle("Synthesis") + .task { + do { + try await viewModel.generateSynthesis(from: entry) + } catch { + print(error) + } + } + } +} + +// #Preview { +// SynthesisEntryView(entry: .) +// } diff --git a/App/Features/AI/Synthesis/SynthesisEntryViewModel.swift b/App/Features/AI/Synthesis/SynthesisEntryViewModel.swift new file mode 100644 index 00000000..71c85a09 --- /dev/null +++ b/App/Features/AI/Synthesis/SynthesisEntryViewModel.swift @@ -0,0 +1,27 @@ +// +// SynthesisEntryViewModel.swift +// wallabag +// +// Created by maxime marinel on 22/01/2024. +// + +import Factory +import Foundation + +final class SynthesisEntryViewModel: ObservableObject { + @Injected(\.chatAssistant) private var chatAssistant + @Published var synthesis = "" + @Published var isLoading = false + + @MainActor + func generateSynthesis(from entry: Entry) async throws { + defer { + isLoading = false + } + isLoading = true + + guard let content = entry.content?.withoutHTML else { return } + + synthesis = try await chatAssistant.generateSynthesis(content: content) + } +} diff --git a/App/Features/AI/Tag/TagSuggestionView.swift b/App/Features/AI/Tag/TagSuggestionView.swift new file mode 100644 index 00000000..8efdeeca --- /dev/null +++ b/App/Features/AI/Tag/TagSuggestionView.swift @@ -0,0 +1,71 @@ +import Factory +import SwiftUI + +struct TagSuggestionView: View { + @Environment(\.dismiss) private var dismiss + @StateObject private var viewModel = TagSuggestionViewModel() + + let entry: Entry + + var body: some View { + VStack { + if viewModel.isLoading { + Text("Your assistant is working") + ProgressView() + } else { + List { + ForEach(viewModel.suggestions, id: \.self) { suggestion in + Button(action: { + if viewModel.tagSelections.contains(suggestion) { + viewModel.tagSelections.remove(suggestion) + } else { + viewModel.tagSelections.insert(suggestion) + } + }, label: { + HStack { + Text(suggestion) + .padding() + .fontDesign(.serif) + Spacer() + if viewModel.tagSelections.contains(suggestion) { + Image(systemName: "checkmark.circle.fill") + } else { + Image(systemName: "circle") + } + } + }) + } + } + .listStyle(.plain) + if !viewModel.tagSelections.isEmpty { + Button(action: { + Task { + try? await viewModel.addTags(to: entry) + dismiss() + } + }, label: { + if viewModel.addingTags { + ProgressView() + } else { + Text("Add \(viewModel.tagSelections.count.formatted()) tags") + } + }) + .buttonStyle(.borderedProminent) + .disabled(viewModel.addingTags) + } + } + } + .navigationTitle("Tag suggestion") + .task { + do { + try await viewModel.generateTags(from: entry) + } catch { + print(error) + } + } + } +} + +// #Preview { +// SynthesisEntryView(entry: .) +// } diff --git a/App/Features/AI/Tag/TagSuggestionViewModel.swift b/App/Features/AI/Tag/TagSuggestionViewModel.swift new file mode 100644 index 00000000..5cbb57d3 --- /dev/null +++ b/App/Features/AI/Tag/TagSuggestionViewModel.swift @@ -0,0 +1,41 @@ +// +// TagSuggestionViewModel.swift +// wallabag +// +// Created by maxime marinel on 22/01/2024. +// + +import Factory +import Foundation + +final class TagSuggestionViewModel: ObservableObject { + @Injected(\.wallabagSession) private var wallabagSession + @Injected(\.chatAssistant) private var chatAssistant + @Published var suggestions: [String] = [] + @Published var isLoading = false + @Published var addingTags = false + @Published var tagSelections = Set() + + @MainActor + func generateTags(from entry: Entry) async throws { + defer { + isLoading = false + } + isLoading = true + + guard let content = entry.content?.withoutHTML else { return } + + suggestions = try await chatAssistant.generateTags(content: content) + } + + @MainActor + func addTags(to entry: Entry) async throws { + defer { + addingTags = false + } + addingTags = true + for tag in tagSelections { + wallabagSession.add(tag: tag, for: entry) + } + } +} diff --git a/App/Features/Entry/EntriesView.swift b/App/Features/Entry/EntriesView.swift index 3b5befc1..41268ed1 100644 --- a/App/Features/Entry/EntriesView.swift +++ b/App/Features/Entry/EntriesView.swift @@ -46,6 +46,10 @@ struct EntriesView: View { Label("Don", systemImage: "heart") }) Divider() + NavigationLink(value: RoutePath.wallabagPlus) { + Label("wallabag Plus", systemImage: "hands.and.sparkles") + } + Divider() Button(action: { router.path.append(RoutePath.setting) }, label: { diff --git a/App/Features/Entry/EntryView.swift b/App/Features/Entry/EntryView.swift index 2be5793a..4b5aaa6a 100644 --- a/App/Features/Entry/EntryView.swift +++ b/App/Features/Entry/EntryView.swift @@ -52,6 +52,20 @@ struct EntryView: View { FontSizeSelectorView() .buttonStyle(.plain) } + ToolbarItem(placement: toolbarPlacement) { + Menu(content: { + NavigationLink(value: RoutePath.synthesis(entry), label: { + Text("Synthesis") + }) + NavigationLink(value: RoutePath.tags(entry), label: { + Text("Suggest tag") + }) + }, label: { + Label("Help assistant", systemImage: "hands.and.sparkles") + .foregroundColor(.primary) + .labelStyle(.iconOnly) + }) + } } .actionSheet(isPresented: $showDeleteConfirm) { ActionSheet( diff --git a/App/Features/Entry/Picture/ImageCache.swift b/App/Features/Entry/Picture/ImageCache.swift index 656e9be6..cecf5e86 100644 --- a/App/Features/Entry/Picture/ImageCache.swift +++ b/App/Features/Entry/Picture/ImageCache.swift @@ -60,8 +60,8 @@ import Foundation let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] do { let files = try FileManager.default.contentsOfDirectory(atPath: url.path) - try files.forEach { - try FileManager.default.removeItem(atPath: url.appendingPathComponent($0).path) + for file in files { + try FileManager.default.removeItem(atPath: url.appendingPathComponent(file).path) } } catch { print("Error in cache purge") diff --git a/App/Features/Router/Route.swift b/App/Features/Router/Route.swift index 65f71e92..35de8d71 100644 --- a/App/Features/Router/Route.swift +++ b/App/Features/Router/Route.swift @@ -5,7 +5,10 @@ enum RoutePath: Hashable { case registration case addEntry case entry(Entry) + case synthesis(Entry) + case tags(Entry) case tips case about case setting + case wallabagPlus } diff --git a/App/Features/Router/RouteSwiftUIExtension.swift b/App/Features/Router/RouteSwiftUIExtension.swift index 49944905..0a93200f 100644 --- a/App/Features/Router/RouteSwiftUIExtension.swift +++ b/App/Features/Router/RouteSwiftUIExtension.swift @@ -16,14 +16,22 @@ extension View { AddEntryView() case let .entry(entry): EntryView(entry: entry) + case let .synthesis(entry): + SynthesisEntryView(entry: entry) + .wallabagPlusProtected() + case let .tags(entry): + TagSuggestionView(entry: entry) + .wallabagPlusProtected() case .about: AboutView() case .tips: TipView() case .setting: SettingView() - default: - Text("test") + case .wallabagPlus: + WallabagPlusView() + case .registration: + RegistrationView() } } } diff --git a/App/Features/Sync/AppSync.swift b/App/Features/Sync/AppSync.swift index 4535f230..82ab9bcd 100644 --- a/App/Features/Sync/AppSync.swift +++ b/App/Features/Sync/AppSync.swift @@ -58,12 +58,12 @@ extension AppSync { } private func handleEntries(_ wallabagEntries: [WallabagEntry]) { - wallabagEntries.forEach { wallabagEntry in - self.entriesSynced.append(wallabagEntry.id) - if let entry = try? self.backgroundContext.fetch(Entry.fetchOneById(wallabagEntry.id)).first { - self.update(entry, with: wallabagEntry) + for wallabagEntry in wallabagEntries { + entriesSynced.append(wallabagEntry.id) + if let entry = try? backgroundContext.fetch(Entry.fetchOneById(wallabagEntry.id)).first { + update(entry, with: wallabagEntry) } else { - self.insert(wallabagEntry) + insert(wallabagEntry) } } } @@ -122,15 +122,15 @@ extension AppSync { private func synchronizeTags() async { do { - try await fetchTags().forEach { wallabagTag in - if let tag = try? self.backgroundContext.fetch(Tag.fetchOneById(wallabagTag.id)).first { - self.tags[tag.id] = tag + for wallabagTag in try await fetchTags() { + if let tag = try? backgroundContext.fetch(Tag.fetchOneById(wallabagTag.id)).first { + tags[tag.id] = tag } else { - let tag = Tag(context: self.backgroundContext) + let tag = Tag(context: backgroundContext) tag.id = wallabagTag.id tag.label = wallabagTag.label tag.slug = wallabagTag.slug - self.tags[wallabagTag.id] = tag + tags[wallabagTag.id] = tag } } } catch _ {} diff --git a/App/Features/WallabagPlus/WallabagPlusProtectedModifier.swift b/App/Features/WallabagPlus/WallabagPlusProtectedModifier.swift new file mode 100644 index 00000000..8452b3f4 --- /dev/null +++ b/App/Features/WallabagPlus/WallabagPlusProtectedModifier.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct WallabagPlusProtectedModifier: ViewModifier { + @EnvironmentObject private var wallabagPlusStore: WallabagPlusStore + + func body(content: Content) -> some View { + VStack { + if wallabagPlusStore.proUnlocked { + content + } else { + Image("logo") + .resizable() + .scaledToFit() + Text("Sorry, this feature require you subscribe to wallabag Plus") + .fontDesign(.rounded) + } + } + } +} + +extension View { + func wallabagPlusProtected() -> some View { + modifier(WallabagPlusProtectedModifier()) + } +} diff --git a/App/Features/WallabagPlus/WallabagPlusStore.swift b/App/Features/WallabagPlus/WallabagPlusStore.swift new file mode 100644 index 00000000..fe8ed77a --- /dev/null +++ b/App/Features/WallabagPlus/WallabagPlusStore.swift @@ -0,0 +1,60 @@ +// +// WallabagPlusStore.swift +// wallabag +// +// Created by maxime marinel on 11/01/2024. +// + +import Foundation +import StoreKit + +final class WallabagPlusStore: ObservableObject { + private var obs: Task? + + let groupID = "21433277" + + @Published var proUnlocked = false + + init() { + obs = observeTransactionUpdates() + restorePurchase() + } + + deinit { + obs?.cancel() + } + + private func observeTransactionUpdates() -> Task { + Task(priority: .background) { + for await verification in Transaction.updates { + await finish(verification) + } + } + } + + func restorePurchase() { + Task { + if let result = await Transaction.latest(for: "wallabagplus") { + await finish(result) + } + } + } + + @MainActor + func finish(_ result: VerificationResult) async { + switch result { + case .unverified: + proUnlocked = false + case let .verified(signedType): + await signedType.finish() + let status = await signedType.subscriptionStatus + proUnlocked = status?.state == .subscribed + } + } + + func handleStatues(_ statuses: [Product.SubscriptionInfo.Status]) async { + guard let transtaction = statuses.last?.transaction else { return } + + await finish(transtaction) + } +} diff --git a/App/Features/WallabagPlus/WallabagPlusSubscribeView.swift b/App/Features/WallabagPlus/WallabagPlusSubscribeView.swift new file mode 100644 index 00000000..21940587 --- /dev/null +++ b/App/Features/WallabagPlus/WallabagPlusSubscribeView.swift @@ -0,0 +1,39 @@ +// +// WallabagPlusSubscribeView.swift +// wallabag +// +// Created by maxime marinel on 11/01/2024. +// + +import StoreKit +import SwiftUI + +struct WallabagPlusSubscribeView: View { + @BundleKey("PRIVACY_URL") + private var privacyURL + + @BundleKey("TERMS_OF_USE_URL") + private var termsOfUseURL + + @EnvironmentObject var wallabagPlusStore: WallabagPlusStore + + var body: some View { + SubscriptionStoreView(groupID: wallabagPlusStore.groupID, visibleRelationships: .all) + .subscriptionStoreButtonLabel(.multiline) + .storeButton(.visible, for: .restorePurchases) + .subscriptionStorePolicyDestination(url: privacyURL.url!, for: .privacyPolicy) + .subscriptionStorePolicyDestination(url: termsOfUseURL.url!, for: .termsOfService) + .subscriptionStoreControlStyle(.prominentPicker) + .onInAppPurchaseCompletion { _, result in + if case let .success(.success(transaction)) = result { + Task { + await wallabagPlusStore.finish(transaction) + } + } + } + } +} + +#Preview { + WallabagPlusSubscribeView() +} diff --git a/App/Features/WallabagPlus/WallabagPlusView.swift b/App/Features/WallabagPlus/WallabagPlusView.swift new file mode 100644 index 00000000..7d87bd3f --- /dev/null +++ b/App/Features/WallabagPlus/WallabagPlusView.swift @@ -0,0 +1,56 @@ +import SwiftUI + +struct WallabagPlusView: View { + @State private var showSubscriptionView = false + + var body: some View { + VStack(alignment: .leading) { + GroupBox("What is wallabag Plus ?") { + Text("Wallabag plus offert premium feature powered by AI") + .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading) + } + GroupBox("wallabag Plus is available on my instance ?") { + Text("No, wallabag plus is a premium feature only available on your iOS devices") + .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading) + Text("Wallabag Plus is not affiliate with any other service.") + .font(.footnote) + } + GroupBox("What is included with wallabag Plus") { + Grid(alignment: .leading, horizontalSpacing: 10, verticalSpacing: 10) { + GridRow { + Image(systemName: "checkmark.circle.fill") + Text("Generate synthesis") + } + GridRow { + Image(systemName: "checkmark.circle.fill") + Text("Suggest tag from entry") + } + } + .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading) + .padding() + } + GroupBox("Privacy") { + Text("When you use wallabag Plus, your entry will be sent to openAI") + .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading) + } + Spacer() + + Button(action: { + showSubscriptionView = true + }, label: { + Text("Subscribe") + }) + .buttonStyle(.borderedProminent) + .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .center) + } + .padding() + .fullScreenCover(isPresented: $showSubscriptionView, content: { + WallabagPlusSubscribeView() + }) + .navigationTitle("Wallabag Plus") + } +} + +#Preview { + WallabagPlusView() +} diff --git a/App/Info.plist b/App/Info.plist index 243fa577..e5402546 100644 --- a/App/Info.plist +++ b/App/Info.plist @@ -6,6 +6,10 @@ DOCUMENTATION_URL https://doc.wallabag.org/en/apps/ios.html + PRIVACY_URL + https://www.district-web.fr/wallabag-reader/privacy + TERMS_OF_USE_URL + https://www.district-web.fr/wallabag-reader/privacy CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -20,6 +24,8 @@ $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString $(MARKETING_VERSION) + GPTBACK_KEY + $(GPTBACK_KEY) CFBundleVersion 319 LSRequiresIPhoneOS diff --git a/App/Lib/DependencyInjection.swift b/App/Lib/DependencyInjection.swift index 4b8d86b9..c83760a9 100644 --- a/App/Lib/DependencyInjection.swift +++ b/App/Lib/DependencyInjection.swift @@ -79,4 +79,17 @@ extension Container { return kit }.scope(.singleton) } + + var chatAssistant: Factory { + self { + ChatAssistant() + } + } + + var wallabagPlusStore: Factory { + self { + WallabagPlusStore() + } + .singleton + } } diff --git a/App/Lib/WallabagError.swift b/App/Lib/WallabagError.swift index a7db405f..71b3d2c2 100644 --- a/App/Lib/WallabagError.swift +++ b/App/Lib/WallabagError.swift @@ -9,13 +9,13 @@ extension WallabagError: LocalizedError { var localizedDescription: String { switch self { case let .syncError(error): - return "\(error)" + "\(error)" case let .wallabagKitError(error): switch error { // case let .jsonError(json): // return json.errorDescription default: - return error.localizedDescription + error.localizedDescription } } } diff --git a/App/PropertyWrapper/BundleKey.swift b/App/PropertyWrapper/BundleKey.swift index 1a322d56..ba90f4ac 100644 --- a/App/PropertyWrapper/BundleKey.swift +++ b/App/PropertyWrapper/BundleKey.swift @@ -1,7 +1,7 @@ import Foundation @propertyWrapper -class BundleKey { +struct BundleKey { let key: String var wrappedValue: String { diff --git a/App/WallabagApp.swift b/App/WallabagApp.swift index e046f211..c4291ebb 100644 --- a/App/WallabagApp.swift +++ b/App/WallabagApp.swift @@ -13,6 +13,7 @@ struct WallabagApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate #endif + @InjectedObject(\.wallabagPlusStore) private var wallabagPlusStore @Injected(\.appState) private var appState @Injected(\.router) private var router #if os(iOS) @@ -35,8 +36,15 @@ struct WallabagApp: App { .environmentObject(errorHandler) .environmentObject(appSync) .environmentObject(appSetting) + .environmentObject(wallabagPlusStore) .environment(\.managedObjectContext, coreData.viewContext) - }.onChange(of: scenePhase) { state in + .subscriptionStatusTask(for: wallabagPlusStore.groupID) { task in + _ = await task.map { statues in + await wallabagPlusStore.handleStatues(statues) + } + } + } + .onChange(of: scenePhase) { state in if state == .active { appState.initSession() #if os(iOS) diff --git a/App/WallabagStoreKit.storekit b/App/WallabagStoreKit.storekit new file mode 100644 index 00000000..434edbbe --- /dev/null +++ b/App/WallabagStoreKit.storekit @@ -0,0 +1,124 @@ +{ + "identifier" : "2657AEEA", + "nonRenewingSubscriptions" : [ + + ], + "products" : [ + { + "displayPrice" : "0.99", + "familyShareable" : false, + "internalID" : "1247187745", + "localizations" : [ + { + "description" : "Cette application est développée sur le temps libre, elle est gratuite et la restera ainsi.Mais vous pouvez contribuer financièrement en faisant un don chaque fois que vous souhaitez soutenir le projet.", + "displayName" : "Don", + "locale" : "fr" + }, + { + "description" : "Diese Anwendung ist auf freie Zeit entwickelt, es ist kostenlos und wird so bleiben. Aber Sie können finanziell beitragen, indem Sie eine Spende machen, wann immer Sie das Projekt unterstützen möchte", + "displayName" : "Geschenk", + "locale" : "de" + }, + { + "description" : "This application is developed on free time, it is free and will remain so. But you can contribute financially by making a donation whenever you want to support the project.", + "displayName" : "Tips", + "locale" : "en_US" + } + ], + "productID" : "tips1", + "referenceName" : "tips1", + "type" : "Consumable" + } + ], + "settings" : { + "_applicationInternalID" : "1170800946", + "_developerTeamID" : "G97URPCGB8", + "_failTransactionsEnabled" : false, + "_lastSynchronizedDate" : 726670179.27365196, + "_locale" : "en_US", + "_storefront" : "USA", + "_storeKitErrors" : [ + { + "current" : null, + "enabled" : false, + "name" : "Load Products" + }, + { + "current" : null, + "enabled" : false, + "name" : "Purchase" + }, + { + "current" : null, + "enabled" : false, + "name" : "Verification" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Store Sync" + }, + { + "current" : null, + "enabled" : false, + "name" : "Subscription Status" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Transaction" + }, + { + "current" : null, + "enabled" : false, + "name" : "Manage Subscriptions Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Refund Request Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Offer Code Redeem Sheet" + } + ] + }, + "subscriptionGroups" : [ + { + "id" : "21433277", + "localizations" : [ + + ], + "name" : "Wallabag", + "subscriptions" : [ + { + "adHocOffers" : [ + + ], + "codeOffers" : [ + + ], + "displayPrice" : "2.99", + "familyShareable" : false, + "groupNumber" : 1, + "internalID" : "6475806829", + "introductoryOffer" : null, + "localizations" : [ + + ], + "productID" : "wallabagplus", + "recurringSubscriptionPeriod" : "P1M", + "referenceName" : "Wallabag plus", + "subscriptionGroupID" : "21433277", + "type" : "RecurringSubscription" + } + ] + } + ], + "version" : { + "major" : 3, + "minor" : 0 + } +} diff --git a/Dangerfile.swift b/Dangerfile.swift index 3f3db838..649618d7 100644 --- a/Dangerfile.swift +++ b/Dangerfile.swift @@ -1,2 +1,3 @@ import Danger + let danger = Danger() diff --git a/Intents/AddEntryIntent.swift b/Intents/AddEntryIntent.swift index 6a9be310..3cf5aa66 100644 --- a/Intents/AddEntryIntent.swift +++ b/Intents/AddEntryIntent.swift @@ -2,10 +2,9 @@ import AppIntents import WallabagKit struct AddEntryIntent: WallabagIntent { - static var title: LocalizedStringResource = "Add Entry" - static var description: IntentDescription = IntentDescription("Add entry to your instance") + static var description: IntentDescription = .init("Add entry to your instance") @Parameter(title: "Url") var url: URL @@ -18,7 +17,7 @@ struct AddEntryIntent: WallabagIntent { _ = try await kit.send(to: WallabagEntryEndpoint.add(url: url.absoluteString)) .receive(on: DispatchQueue.main) .values - .first(where: {(_: WallabagEntry) in true}) + .first(where: { (_: WallabagEntry) in true }) return .result() } } diff --git a/Intents/WallabagIntent.swift b/Intents/WallabagIntent.swift index cfbd8449..2d861f59 100644 --- a/Intents/WallabagIntent.swift +++ b/Intents/WallabagIntent.swift @@ -1,6 +1,6 @@ -import WallabagKit import AppIntents import SharedLib +import WallabagKit protocol WallabagIntent: AppIntent {} diff --git a/SharedLib/Sources/SharedLib/Features/RetrieveMode/RetrieveMode.swift b/SharedLib/Sources/SharedLib/Features/RetrieveMode/RetrieveMode.swift index d985c739..b11e55a8 100644 --- a/SharedLib/Sources/SharedLib/Features/RetrieveMode/RetrieveMode.swift +++ b/SharedLib/Sources/SharedLib/Features/RetrieveMode/RetrieveMode.swift @@ -24,26 +24,26 @@ public enum RetrieveMode: String, CaseIterable { public var settingCase: String { switch self { case .allArticles: - return "allArticles" + "allArticles" case .archivedArticles: - return "archivedArticles" + "archivedArticles" case .unarchivedArticles: - return "unarchivedArticles" + "unarchivedArticles" case .starredArticles: - return "starredArticles" + "starredArticles" } } public func predicate() -> NSPredicate { switch self { case .unarchivedArticles: - return NSPredicate(format: "isArchived == NO") + NSPredicate(format: "isArchived == NO") case .starredArticles: - return NSPredicate(format: "isStarred == YES") + NSPredicate(format: "isStarred == YES") case .archivedArticles: - return NSPredicate(format: "isArchived == YES") + NSPredicate(format: "isArchived == YES") case .allArticles: - return NSPredicate(value: true) + NSPredicate(value: true) } } } diff --git a/WallabagKit/Sources/WallabagKit/Endpoint/WallabagEntryEndpoint.swift b/WallabagKit/Sources/WallabagKit/Endpoint/WallabagEntryEndpoint.swift index 9d95d0bb..d7be311a 100644 --- a/WallabagKit/Sources/WallabagKit/Endpoint/WallabagEntryEndpoint.swift +++ b/WallabagKit/Sources/WallabagKit/Endpoint/WallabagEntryEndpoint.swift @@ -12,15 +12,15 @@ public enum WallabagEntryEndpoint: WallabagKitEndpoint { public func method() -> HttpMethod { switch self { case .get: - return .get + .get case .add, .addTag: - return .post + .post case .delete, .deleteTag: - return .delete + .delete case .update: - return .patch + .patch case .reload: - return .patch + .patch } } @@ -54,15 +54,15 @@ public enum WallabagEntryEndpoint: WallabagKitEndpoint { switch self { case let .add(url): // swiftlint:disable:next force_try - return try! JSONSerialization.data(withJSONObject: ["url": url], options: .prettyPrinted) + try! JSONSerialization.data(withJSONObject: ["url": url], options: .prettyPrinted) case let .update(_, parameters): // swiftlint:disable:next force_try - return try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) + try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) case let .addTag(tag, _): // swiftlint:disable:next force_try - return try! JSONSerialization.data(withJSONObject: ["tags": tag], options: .prettyPrinted) + try! JSONSerialization.data(withJSONObject: ["tags": tag], options: .prettyPrinted) default: - return "".data(using: .utf8)! + "".data(using: .utf8)! } } diff --git a/WallabagKit/Sources/WallabagKit/Endpoint/WallabagOAuth.swift b/WallabagKit/Sources/WallabagKit/Endpoint/WallabagOAuth.swift index 05fd6948..bfd39006 100644 --- a/WallabagKit/Sources/WallabagKit/Endpoint/WallabagOAuth.swift +++ b/WallabagKit/Sources/WallabagKit/Endpoint/WallabagOAuth.swift @@ -8,7 +8,7 @@ enum WallabagOauth: WallabagKitEndpoint { func endpoint() -> String { switch self { case .request: - return "/oauth/v2/token" + "/oauth/v2/token" } } @@ -16,7 +16,7 @@ enum WallabagOauth: WallabagKitEndpoint { switch self { case let .request(clientId, clientSecret, username, password): // swiftlint:disable:next force_try - return try! JSONSerialization.data(withJSONObject: [ + try! JSONSerialization.data(withJSONObject: [ "grant_type": "password", "client_id": clientId, "client_secret": clientSecret, diff --git a/bagit/ShareExtensionError.swift b/bagit/ShareExtensionError.swift index 9487463b..c2c12929 100644 --- a/bagit/ShareExtensionError.swift +++ b/bagit/ShareExtensionError.swift @@ -9,13 +9,13 @@ enum ShareExtensionError: Error, LocalizedError { var localizedDescription: String { switch self { case .unregistredApp: - return "App not registred or configured" + "App not registred or configured" case .authError: - return "Error during auth" + "Error during auth" case .retrievingURL: - return "Error retrieve url from extension" + "Error retrieve url from extension" case .duringAdding: - return "Error during pushing to your wallabag server" + "Error during pushing to your wallabag server" } } } diff --git a/fastlane/SnapshotHelper.swift b/fastlane/SnapshotHelper.swift index 2d3505b9..1e0c142b 100644 --- a/fastlane/SnapshotHelper.swift +++ b/fastlane/SnapshotHelper.swift @@ -44,9 +44,9 @@ enum SnapshotError: Error, CustomDebugStringConvertible { var debugDescription: String { switch self { case .cannotFindSimulatorHomeDirectory: - return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable." + "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable." case .cannotRunOnPhysicalDevice: - return "Can't use Snapshot on a physical device." + "Can't use Snapshot on a physical device." } } } diff --git a/openapi-generator-config.yaml b/openapi-generator-config.yaml new file mode 100644 index 00000000..2f510677 --- /dev/null +++ b/openapi-generator-config.yaml @@ -0,0 +1,7 @@ +generate: + - types + - client + +filter: + tags: + - wallabag diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 00000000..a01709cb --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,119 @@ +openapi: 3.1.0 +info: + title: Back GPT + version: 1.0.0 +servers: + - url: http://localhost:8080 + - url: https://gpt.district-web.com +tags: + - name: wallabag + description: Everything about wallabag + - name: opinion + description: Everything about opinion +paths: + /wallabag/synthesis: + post: + tags: + - wallabag + summary: Generate synthesis from entry + operationId: wallabagSynthesis + requestBody: + description: Generate synthesis from entry + content: + application/json: + schema: + $ref: '#/components/schemas/WallabagSynthesisQuery' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WallabagSynthesisResponse' + /wallabag/tags: + post: + tags: + - wallabag + summary: Generate tags from entry + operationId: wallabagTags + requestBody: + description: Generate tags from entry + content: + application/json: + schema: + $ref: '#/components/schemas/WallabagTagQuery' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WallabagTagResponse' + /opinion/review: + post: + tags: + - opinion + summary: Generate response from entry + operationId: opinionReview + requestBody: + description: Generate response from entry + content: + application/json: + schema: + $ref: '#/components/schemas/OpinionReview' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/OpinionReview' +components: + schemas: + WallabagSynthesisQuery: + type: object + properties: + body: + type: string + examples: ['My content'] + language: + type: string + examples: [FR] + WallabagSynthesisResponse: + type: object + properties: + content: + type: string + examples: ['My content'] + WallabagTagQuery: + type: object + properties: + body: + type: string + examples: ['My content'] + language: + type: string + examples: [FR] + WallabagTagResponse: + type: object + properties: + tags: + type: array + items: + type: string + examples: ['My content'] + OpinionReview: + type: object + properties: + content: + type: string + examples: ['My content'] + securitySchemes: + bearerAuth: + type: http + scheme: bearer +security: + - bearerAuth: [] diff --git a/wallabag.xcodeproj/project.pbxproj b/wallabag.xcodeproj/project.pbxproj index 499789e0..6fa735e0 100644 --- a/wallabag.xcodeproj/project.pbxproj +++ b/wallabag.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 094AA03E2629EB1F006E5605 /* wallabagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094AA03D2629EB1F006E5605 /* wallabagTests.swift */; }; 094AA05A2629EB60006E5605 /* ImageCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094A9FED2629E6BB006E5605 /* ImageCacheTests.swift */; }; 094AA0692629EB98006E5605 /* ImageDownloaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094AA00F2629E739006E5605 /* ImageDownloaderTests.swift */; }; + 094BE2B52B5E94F900DFBF5A /* SynthesisEntryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094BE2B42B5E94F900DFBF5A /* SynthesisEntryViewModel.swift */; }; + 094BE2B92B5E952C00DFBF5A /* TagSuggestionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094BE2B82B5E952C00DFBF5A /* TagSuggestionViewModel.swift */; }; 0951C61829CC2EB000D8E8C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0951C61729CC2EB000D8E8C6 /* Assets.xcassets */; }; 09564E0D2851C57200D39E95 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09564E0C2851C57200D39E95 /* SettingView.swift */; }; 09644AD125C94825000FFDA1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09644AD025C94825000FFDA1 /* ShareViewController.swift */; }; @@ -78,17 +80,28 @@ 09644D1025C9872F000FFDA1 /* BundleKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09644D0F25C9872F000FFDA1 /* BundleKey.swift */; }; 09644D1825C98755000FFDA1 /* AppDelegateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09644D1725C98755000FFDA1 /* AppDelegateExtension.swift */; }; 09644D2025C98782000FFDA1 /* wallabagStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 09644D1E25C98782000FFDA1 /* wallabagStore.xcdatamodeld */; }; + 097DD8FA2B4C0DB8005D3930 /* Factory in Frameworks */ = {isa = PBXBuildFile; productRef = 097DD8F92B4C0DB8005D3930 /* Factory */; }; + 097DD8FC2B4C0E02005D3930 /* OpenAPIRuntime in Frameworks */ = {isa = PBXBuildFile; productRef = 097DD8FB2B4C0E02005D3930 /* OpenAPIRuntime */; }; + 097DD8FE2B4C0E08005D3930 /* OpenAPIURLSession in Frameworks */ = {isa = PBXBuildFile; productRef = 097DD8FD2B4C0E08005D3930 /* OpenAPIURLSession */; }; + 097DD9042B4C0EE0005D3930 /* openapi-generator-config.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 097DD9022B4C0E78005D3930 /* openapi-generator-config.yaml */; }; + 097DD9052B4C0EE0005D3930 /* openapi.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 097DD9012B4C0E62005D3930 /* openapi.yaml */; }; 097F81EB25CB18BA006C85F6 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097F81DF25CB187B006C85F6 /* Router.swift */; }; 09800D8F29B9DEFE00DAB403 /* SharedLib in Frameworks */ = {isa = PBXBuildFile; productRef = 09800D8E29B9DEFE00DAB403 /* SharedLib */; }; 098BDD0829BF04E3003DF719 /* RouteSwiftUIExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BDD0729BF04E3003DF719 /* RouteSwiftUIExtension.swift */; }; 098CF4D629CD830E00DEAE50 /* ServerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098CF4D529CD830E00DEAE50 /* ServerViewModelTests.swift */; }; + 09952D112B566EAB0059D24F /* TagSuggestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09952D102B566EAB0059D24F /* TagSuggestionView.swift */; }; + 099CD1292B4FCC6F0029E94A /* WallabagPlusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099CD1282B4FCC6F0029E94A /* WallabagPlusView.swift */; }; + 099CD12C2B4FCDB90029E94A /* WallabagPlusSubscribeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099CD12B2B4FCDB90029E94A /* WallabagPlusSubscribeView.swift */; }; + 099CD1312B501D950029E94A /* WallabagPlusStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099CD1302B501D950029E94A /* WallabagPlusStore.swift */; }; + 099CD1372B516DA30029E94A /* WallabagPlusProtectedModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099CD1362B516DA30029E94A /* WallabagPlusProtectedModifier.swift */; }; 09AD75A12624F93D00708A1E /* article.html in Resources */ = {isa = PBXBuildFile; fileRef = 09AD759D2624F93D00708A1E /* article.html */; }; 09AD75A22624F93D00708A1E /* main.css in Resources */ = {isa = PBXBuildFile; fileRef = 09AD759E2624F93D00708A1E /* main.css */; }; 09AD75A32624F93D00708A1E /* ratatouille.css in Resources */ = {isa = PBXBuildFile; fileRef = 09AD759F2624F93D00708A1E /* ratatouille.css */; }; 09AD75A42624F93D00708A1E /* justify.css in Resources */ = {isa = PBXBuildFile; fileRef = 09AD75A02624F93D00708A1E /* justify.css */; }; 09AD75BE2624FEEE00708A1E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 09AD75C02624FEEE00708A1E /* Localizable.strings */; }; - 09AE6ECC29B13AAE0025DF93 /* WallabagKit in Frameworks */ = {isa = PBXBuildFile; productRef = 09AE6ECB29B13AAE0025DF93 /* WallabagKit */; }; - 09AEBEA029B1D2C500050BBE /* Factory in Frameworks */ = {isa = PBXBuildFile; productRef = 09AEBE9F29B1D2C500050BBE /* Factory */; }; + 09B8C3A62B27AD2D002AEA2C /* ChatAssistant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B8C3A52B27AD2D002AEA2C /* ChatAssistant.swift */; }; + 09B8C3A82B27AF62002AEA2C /* SynthesisEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B8C3A72B27AF62002AEA2C /* SynthesisEntryView.swift */; }; + 09B977CD2B5E984900512851 /* WallabagKit in Frameworks */ = {isa = PBXBuildFile; productRef = 09AE6ECB29B13AAE0025DF93 /* WallabagKit */; }; 09BCBBD4282BD46A00B234CB /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BCBBD3282BD46A00B234CB /* RouterTests.swift */; }; 09BE0AF42A9F45E900193FBF /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE0AF32A9F45E900193FBF /* View+Extension.swift */; }; 09C34C7C29B1398B00B5C927 /* SharedLib in Frameworks */ = {isa = PBXBuildFile; productRef = 09C34C7B29B1398B00B5C927 /* SharedLib */; }; @@ -140,6 +153,8 @@ 094AA03B2629EB1F006E5605 /* wallabagTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = wallabagTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 094AA03D2629EB1F006E5605 /* wallabagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = wallabagTests.swift; sourceTree = ""; }; 094AA03F2629EB1F006E5605 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 094BE2B42B5E94F900DFBF5A /* SynthesisEntryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynthesisEntryViewModel.swift; sourceTree = ""; }; + 094BE2B82B5E952C00DFBF5A /* TagSuggestionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSuggestionViewModel.swift; sourceTree = ""; }; 0951C61729CC2EB000D8E8C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 09564E0C2851C57200D39E95 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = ""; }; 09644ACE25C94825000FFDA1 /* bagit.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = bagit.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -208,9 +223,17 @@ 09644D0F25C9872F000FFDA1 /* BundleKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BundleKey.swift; sourceTree = ""; }; 09644D1725C98755000FFDA1 /* AppDelegateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateExtension.swift; sourceTree = ""; }; 09644D1F25C98782000FFDA1 /* wallabagStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = wallabagStore.xcdatamodel; sourceTree = ""; }; + 097DD9012B4C0E62005D3930 /* openapi.yaml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = openapi.yaml; sourceTree = ""; }; + 097DD9022B4C0E78005D3930 /* openapi-generator-config.yaml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = "openapi-generator-config.yaml"; sourceTree = ""; }; 097F81DF25CB187B006C85F6 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 098BDD0729BF04E3003DF719 /* RouteSwiftUIExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteSwiftUIExtension.swift; sourceTree = ""; }; 098CF4D529CD830E00DEAE50 /* ServerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerViewModelTests.swift; sourceTree = ""; }; + 09952D102B566EAB0059D24F /* TagSuggestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSuggestionView.swift; sourceTree = ""; }; + 099CD1282B4FCC6F0029E94A /* WallabagPlusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallabagPlusView.swift; sourceTree = ""; }; + 099CD12B2B4FCDB90029E94A /* WallabagPlusSubscribeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallabagPlusSubscribeView.swift; sourceTree = ""; }; + 099CD12E2B5019F40029E94A /* WallabagStoreKit.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = WallabagStoreKit.storekit; sourceTree = ""; }; + 099CD1302B501D950029E94A /* WallabagPlusStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallabagPlusStore.swift; sourceTree = ""; }; + 099CD1362B516DA30029E94A /* WallabagPlusProtectedModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallabagPlusProtectedModifier.swift; sourceTree = ""; }; 09AD75962624F4E800708A1E /* wallabag.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = wallabag.entitlements; sourceTree = ""; }; 09AD759D2624F93D00708A1E /* article.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = article.html; sourceTree = ""; }; 09AD759E2624F93D00708A1E /* main.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = main.css; sourceTree = ""; }; @@ -228,13 +251,15 @@ 09AD75D32624FF5600708A1E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 09AD75D42624FF5F00708A1E /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 09AD75D52624FF6900708A1E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + 09B8C3A02B27AB5F002AEA2C /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + 09B8C3A52B27AD2D002AEA2C /* ChatAssistant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAssistant.swift; sourceTree = ""; }; + 09B8C3A72B27AF62002AEA2C /* SynthesisEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynthesisEntryView.swift; sourceTree = ""; }; 09BCBBD3282BD46A00B234CB /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; 09BE0AF32A9F45E900193FBF /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = ""; }; 09BFB28425C8348E00E12B4D /* wallabag.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wallabag.app; sourceTree = BUILT_PRODUCTS_DIR; }; 09BFB28725C8348E00E12B4D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81505C5A2B5DC21F003B5CDE /* WallabagIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallabagIntent.swift; sourceTree = ""; }; 81505C5C2B5DC23C003B5CDE /* AddEntryIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEntryIntent.swift; sourceTree = ""; }; - 9C1103B626A0852F00E50F26 /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -249,7 +274,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 09AE6ECC29B13AAE0025DF93 /* WallabagKit in Frameworks */, + 09B977CD2B5E984900512851 /* WallabagKit in Frameworks */, 09800D8F29B9DEFE00DAB403 /* SharedLib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -258,10 +283,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 09AEBEA029B1D2C500050BBE /* Factory in Frameworks */, 09644B0F25C94C6B000FFDA1 /* HTMLEntities in Frameworks */, 09C34C7E29B1399100B5C927 /* WallabagKit in Frameworks */, + 097DD8FC2B4C0E02005D3930 /* OpenAPIRuntime in Frameworks */, 09C34C7C29B1398B00B5C927 /* SharedLib in Frameworks */, + 097DD8FE2B4C0E08005D3930 /* OpenAPIURLSession in Frameworks */, + 097DD8FA2B4C0DB8005D3930 /* Factory in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -305,6 +332,24 @@ path = wallabagTests; sourceTree = ""; }; + 094BE2B22B5E94EA00DFBF5A /* Synthesis */ = { + isa = PBXGroup; + children = ( + 09B8C3A72B27AF62002AEA2C /* SynthesisEntryView.swift */, + 094BE2B42B5E94F900DFBF5A /* SynthesisEntryViewModel.swift */, + ); + path = Synthesis; + sourceTree = ""; + }; + 094BE2B62B5E951F00DFBF5A /* Tag */ = { + isa = PBXGroup; + children = ( + 09952D102B566EAB0059D24F /* TagSuggestionView.swift */, + 094BE2B82B5E952C00DFBF5A /* TagSuggestionViewModel.swift */, + ); + path = Tag; + sourceTree = ""; + }; 09644ACF25C94825000FFDA1 /* bagit */ = { isa = PBXGroup; children = ( @@ -346,6 +391,8 @@ 09644B8425C98161000FFDA1 /* Features */ = { isa = PBXGroup; children = ( + 099CD1272B4FCC570029E94A /* WallabagPlus */, + 09B8C3A42B27ACB4002AEA2C /* AI */, 09644C9D25C98615000FFDA1 /* MainView.swift */, 09644C8525C985C4000FFDA1 /* About */, 09644C8D25C985D9000FFDA1 /* BugReport */, @@ -610,6 +657,17 @@ path = Server; sourceTree = ""; }; + 099CD1272B4FCC570029E94A /* WallabagPlus */ = { + isa = PBXGroup; + children = ( + 099CD1282B4FCC6F0029E94A /* WallabagPlusView.swift */, + 099CD12B2B4FCDB90029E94A /* WallabagPlusSubscribeView.swift */, + 099CD1302B501D950029E94A /* WallabagPlusStore.swift */, + 099CD1362B516DA30029E94A /* WallabagPlusProtectedModifier.swift */, + ); + path = WallabagPlus; + sourceTree = ""; + }; 09AD759C2624F93D00708A1E /* html-ressources */ = { isa = PBXGroup; children = ( @@ -621,6 +679,16 @@ path = "html-ressources"; sourceTree = ""; }; + 09B8C3A42B27ACB4002AEA2C /* AI */ = { + isa = PBXGroup; + children = ( + 094BE2B62B5E951F00DFBF5A /* Tag */, + 094BE2B22B5E94EA00DFBF5A /* Synthesis */, + 09B8C3A52B27AD2D002AEA2C /* ChatAssistant.swift */, + ); + path = AI; + sourceTree = ""; + }; 09BCBBD2282BD44100B234CB /* Router */ = { isa = PBXGroup; children = ( @@ -640,8 +708,11 @@ 09BFB27425C8348E00E12B4D = { isa = PBXGroup; children = ( - 81505C592B5DC209003B5CDE /* Intents */, + 097DD9022B4C0E78005D3930 /* openapi-generator-config.yaml */, + 097DD9012B4C0E62005D3930 /* openapi.yaml */, + 09B8C3A02B27AB5F002AEA2C /* Config.xcconfig */, 09AD75962624F4E800708A1E /* wallabag.entitlements */, + 81505C592B5DC209003B5CDE /* Intents */, 09644ACF25C94825000FFDA1 /* bagit */, 09644B1525C94CA6000FFDA1 /* Frameworks */, 09BFB28625C8348E00E12B4D /* App */, @@ -664,7 +735,6 @@ isa = PBXGroup; children = ( 09BFB28725C8348E00E12B4D /* Info.plist */, - 9C1103B626A0852F00E50F26 /* Configuration.storekit */, 09644B5D25C98116000FFDA1 /* AppDelegate.swift */, 09644B5625C9810A000FFDA1 /* WallabagApp.swift */, 097F824C25CB1B17006C85F6 /* Entity */, @@ -676,6 +746,7 @@ 09644BE425C98343000FFDA1 /* PropertyWrapper */, 09644D1E25C98782000FFDA1 /* wallabagStore.xcdatamodeld */, 0951C61729CC2EB000D8E8C6 /* Assets.xcassets */, + 099CD12E2B5019F40029E94A /* WallabagStoreKit.storekit */, ); path = App; sourceTree = ""; @@ -744,6 +815,7 @@ buildRules = ( ); dependencies = ( + 097DD9002B4C0E2A005D3930 /* PBXTargetDependency */, 09644AD725C94825000FFDA1 /* PBXTargetDependency */, ); name = wallabag; @@ -751,7 +823,9 @@ 09644B0E25C94C6B000FFDA1 /* HTMLEntities */, 09C34C7B29B1398B00B5C927 /* SharedLib */, 09C34C7D29B1399100B5C927 /* WallabagKit */, - 09AEBE9F29B1D2C500050BBE /* Factory */, + 097DD8F92B4C0DB8005D3930 /* Factory */, + 097DD8FB2B4C0E02005D3930 /* OpenAPIRuntime */, + 097DD8FD2B4C0E08005D3930 /* OpenAPIURLSession */, ); productName = "wallabag (iOS)"; productReference = 09BFB28425C8348E00E12B4D /* wallabag.app */; @@ -763,8 +837,9 @@ 09BFB27525C8348E00E12B4D /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1240; - LastUpgradeCheck = 1420; + LastUpgradeCheck = 1510; TargetAttributes = { 094AA03A2629EB1F006E5605 = { CreatedOnToolsVersion = 12.4; @@ -800,7 +875,10 @@ mainGroup = 09BFB27425C8348E00E12B4D; packageReferences = ( 09644B0D25C94C6B000FFDA1 /* XCRemoteSwiftPackageReference "swift-html-entities" */, - 09AEBE9E29B1D2C500050BBE /* XCRemoteSwiftPackageReference "Factory" */, + 097DD8F52B4C0CCC005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-generator" */, + 097DD8F62B4C0D3A005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-runtime" */, + 097DD8F72B4C0D54005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-urlsession" */, + 097DD8F82B4C0DB8005D3930 /* XCRemoteSwiftPackageReference "Factory" */, ); productRefGroup = 09BFB28525C8348E00E12B4D /* Products */; projectDirPath = ""; @@ -836,7 +914,9 @@ buildActionMask = 2147483647; files = ( 09AD75A32624F93D00708A1E /* ratatouille.css in Resources */, + 097DD9052B4C0EE0005D3930 /* openapi.yaml in Resources */, 09AD75A12624F93D00708A1E /* article.html in Resources */, + 097DD9042B4C0EE0005D3930 /* openapi-generator-config.yaml in Resources */, 09AD75BE2624FEEE00708A1E /* Localizable.strings in Resources */, 09AD75A42624F93D00708A1E /* justify.css in Resources */, 0951C61829CC2EB000D8E8C6 /* Assets.xcassets in Resources */, @@ -905,6 +985,7 @@ 09644C8F25C985E8000FFDA1 /* BugReportView.swift in Sources */, 09644BFB25C983F8000FFDA1 /* Tag.swift in Sources */, 09644CD625C986C0000FFDA1 /* ClientIdClientSecretView.swift in Sources */, + 09952D112B566EAB0059D24F /* TagSuggestionView.swift in Sources */, 09644B9725C9819F000FFDA1 /* PlayerView.swift in Sources */, 09644CF225C986EE000FFDA1 /* SearchViewModel.swift in Sources */, 09644BA325C981B4000FFDA1 /* ErrorViewModel.swift in Sources */, @@ -914,18 +995,23 @@ 09644B7E25C98152000FFDA1 /* AppState.swift in Sources */, 09644C6E25C985A9000FFDA1 /* DeleteEntryButton.swift in Sources */, 09644B8C25C98176000FFDA1 /* Route.swift in Sources */, + 094BE2B52B5E94F900DFBF5A /* SynthesisEntryViewModel.swift in Sources */, 09644BAD25C98213000FFDA1 /* CoreDataSync.swift in Sources */, 09644BDE25C9833E000FFDA1 /* WallabagSession.swift in Sources */, 09644C7125C985A9000FFDA1 /* FontSizeSelectorView.swift in Sources */, 09644C7D25C985B8000FFDA1 /* ImageCache.swift in Sources */, 09644CC225C98674000FFDA1 /* PasteBoardView.swift in Sources */, + 099CD12C2B4FCDB90029E94A /* WallabagPlusSubscribeView.swift in Sources */, + 09B8C3A82B27AF62002AEA2C /* SynthesisEntryView.swift in Sources */, 09644CC325C98674000FFDA1 /* PasteBoardViewModel.swift in Sources */, 09644C6D25C985A9000FFDA1 /* StarEntryButton.swift in Sources */, 09644C6125C98596000FFDA1 /* EntriesListView.swift in Sources */, + 099CD1372B516DA30029E94A /* WallabagPlusProtectedModifier.swift in Sources */, 09644CF325C986EE000FFDA1 /* SearchView.swift in Sources */, 09564E0D2851C57200D39E95 /* SettingView.swift in Sources */, 09644C5F25C98596000FFDA1 /* EntryRowView.swift in Sources */, 09644CE925C986CC000FFDA1 /* ServerViewModel.swift in Sources */, + 099CD1312B501D950029E94A /* WallabagPlusStore.swift in Sources */, 09644CD725C986C0000FFDA1 /* ClientIdClientSecretViewModel.swift in Sources */, 09644CCB25C9869D000FFDA1 /* RegistrationView.swift in Sources */, 09644CFE25C9870C000FFDA1 /* TagRow.swift in Sources */, @@ -935,6 +1021,8 @@ 09644BB725C98232000FFDA1 /* AppSetting.swift in Sources */, 09644C7F25C985B8000FFDA1 /* ImageDownloaderPublisher.swift in Sources */, 09644CE825C986CC000FFDA1 /* ServerView.swift in Sources */, + 09B8C3A62B27AD2D002AEA2C /* ChatAssistant.swift in Sources */, + 094BE2B92B5E952C00DFBF5A /* TagSuggestionViewModel.swift in Sources */, 09644CFD25C9870C000FFDA1 /* TagListFor.swift in Sources */, 09644BFA25C983F8000FFDA1 /* Podcast.swift in Sources */, 09644BAE25C98213000FFDA1 /* AppSync.swift in Sources */, @@ -942,6 +1030,7 @@ 09644BA225C981B4000FFDA1 /* ErrorView.swift in Sources */, 09644B9825C9819F000FFDA1 /* PlayerPublisher.swift in Sources */, 09644C7C25C985B8000FFDA1 /* ImageDownloader.swift in Sources */, + 099CD1292B4FCC6F0029E94A /* WallabagPlusView.swift in Sources */, 09644D0825C9871A000FFDA1 /* TipView.swift in Sources */, 09644BDD25C9833E000FFDA1 /* CoreData.swift in Sources */, 09644D0925C9871A000FFDA1 /* TipViewModel.swift in Sources */, @@ -976,6 +1065,10 @@ target = 09644ACD25C94825000FFDA1 /* bagit */; targetProxy = 09644AD625C94825000FFDA1 /* PBXContainerItemProxy */; }; + 097DD9002B4C0E2A005D3930 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 097DD8FF2B4C0E2A005D3930 /* OpenAPIGenerator */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1109,6 +1202,7 @@ }; 09BFB2B025C8348E00E12B4D /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 09B8C3A02B27AB5F002AEA2C /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -1160,7 +1254,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MARKETING_VERSION = 7.0.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1172,6 +1266,7 @@ }; 09BFB2B125C8348E00E12B4D /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 09B8C3A02B27AB5F002AEA2C /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -1217,7 +1312,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MARKETING_VERSION = 7.0.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -1239,7 +1334,7 @@ ENABLE_PREVIEWS = YES; INFOPLIST_FILE = App/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1269,7 +1364,7 @@ ENABLE_PREVIEWS = YES; INFOPLIST_FILE = App/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1337,12 +1432,36 @@ minimumVersion = 3.0.200; }; }; - 09AEBE9E29B1D2C500050BBE /* XCRemoteSwiftPackageReference "Factory" */ = { + 097DD8F52B4C0CCC005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-generator" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-openapi-generator"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.0; + }; + }; + 097DD8F62B4C0D3A005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-runtime" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-openapi-runtime"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.0; + }; + }; + 097DD8F72B4C0D54005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-urlsession" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-openapi-urlsession"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; + 097DD8F82B4C0DB8005D3930 /* XCRemoteSwiftPackageReference "Factory" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "git@github.com:hmlongco/Factory.git"; + repositoryURL = "https://github.com/hmlongco/Factory"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 2.0.0; + minimumVersion = 2.3.1; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -1353,6 +1472,26 @@ package = 09644B0D25C94C6B000FFDA1 /* XCRemoteSwiftPackageReference "swift-html-entities" */; productName = HTMLEntities; }; + 097DD8F92B4C0DB8005D3930 /* Factory */ = { + isa = XCSwiftPackageProductDependency; + package = 097DD8F82B4C0DB8005D3930 /* XCRemoteSwiftPackageReference "Factory" */; + productName = Factory; + }; + 097DD8FB2B4C0E02005D3930 /* OpenAPIRuntime */ = { + isa = XCSwiftPackageProductDependency; + package = 097DD8F62B4C0D3A005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-runtime" */; + productName = OpenAPIRuntime; + }; + 097DD8FD2B4C0E08005D3930 /* OpenAPIURLSession */ = { + isa = XCSwiftPackageProductDependency; + package = 097DD8F72B4C0D54005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-urlsession" */; + productName = OpenAPIURLSession; + }; + 097DD8FF2B4C0E2A005D3930 /* OpenAPIGenerator */ = { + isa = XCSwiftPackageProductDependency; + package = 097DD8F52B4C0CCC005D3930 /* XCRemoteSwiftPackageReference "swift-openapi-generator" */; + productName = "plugin:OpenAPIGenerator"; + }; 09800D8E29B9DEFE00DAB403 /* SharedLib */ = { isa = XCSwiftPackageProductDependency; productName = SharedLib; @@ -1361,11 +1500,6 @@ isa = XCSwiftPackageProductDependency; productName = WallabagKit; }; - 09AEBE9F29B1D2C500050BBE /* Factory */ = { - isa = XCSwiftPackageProductDependency; - package = 09AEBE9E29B1D2C500050BBE /* XCRemoteSwiftPackageReference "Factory" */; - productName = Factory; - }; 09C34C7B29B1398B00B5C927 /* SharedLib */ = { isa = XCSwiftPackageProductDependency; productName = SharedLib; diff --git a/wallabag.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/wallabag.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e1c7a8dd..6793343c 100644 --- a/wallabag.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/wallabag.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,34 +1,32 @@ { - "object": { - "pins": [ - { - "package": "HTMLEntities", - "repositoryURL": "https://github.com/IBM-Swift/swift-html-entities.git", - "state": { - "branch": null, - "revision": "2b14531d0c36dbb7c1c45a4d38db9c2e7898a307", - "version": "3.0.200" - } - }, - { - "package": "Swinject", - "repositoryURL": "https://github.com/Swinject/Swinject", - "state": { - "branch": null, - "revision": "8a76d2c74bafbb455763487cc6a08e91bad1f78b", - "version": "2.7.1" - } - }, - { - "package": "WallabagKit", - "repositoryURL": "https://github.com/wallabag/WallabagKit.git", - "state": { - "branch": "master", - "revision": "6f83586b893f876e447ae68d1f7c0508bdc8a3bb", - "version": null - } + "pins" : [ + { + "identity" : "factory", + "kind" : "remoteSourceControl", + "location" : "git@github.com:hmlongco/Factory.git", + "state" : { + "revision" : "8ca11a7bd1ede031e8e6d7a912bb116e2e43961b", + "version" : "2.3.1" } - ] - }, - "version": 1 + }, + { + "identity" : "openaiswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/astrokin/OpenAISwift.git", + "state" : { + "branch" : "fix_400", + "revision" : "db66252954f64c59111abb1932866039a64ef029" + } + }, + { + "identity" : "swift-html-entities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/IBM-Swift/swift-html-entities.git", + "state" : { + "revision" : "2b14531d0c36dbb7c1c45a4d38db9c2e7898a307", + "version" : "3.0.200" + } + } + ], + "version" : 2 } diff --git a/wallabag.xcodeproj/xcshareddata/xcschemes/bagit.xcscheme b/wallabag.xcodeproj/xcshareddata/xcschemes/bagit.xcscheme index b917b70f..e73e487f 100644 --- a/wallabag.xcodeproj/xcshareddata/xcschemes/bagit.xcscheme +++ b/wallabag.xcodeproj/xcshareddata/xcschemes/bagit.xcscheme @@ -1,6 +1,6 @@ + identifier = "../App/WallabagStoreKit.storekit">