From 362b9b7d1bc94b672f3bdc338d09aee7bce03cf6 Mon Sep 17 00:00:00 2001 From: szbk Date: Wed, 11 Feb 2026 18:26:17 +0300 Subject: [PATCH] =?UTF-8?q?feat(ios):=20tab=20tabanl=C4=B1=20navigasyon=20?= =?UTF-8?q?ve=20okuma=20durumu=20takibi=20ekle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Bookibra.xcodeproj/project.pbxproj | 612 +++++++++--------- .../UserInterfaceState.xcuserstate | Bin 20636 -> 24173 bytes ios/Bookibra/App/AppRouter.swift | 10 +- ios/Bookibra/App/BookibraApp.swift | 67 +- .../Components/BookCardView.swift | 68 ++ .../Components/EmptyStateView.swift | 34 + ios/Bookibra/DesignSystem/Theme.swift | 17 + ios/Bookibra/Models/BookRemote.swift | 8 +- ios/Bookibra/Models/LibraryBook.swift | 19 +- ios/Bookibra/Models/ReadingStatus.swift | 34 + .../ViewModels/BookDetailViewModel.swift | 4 +- ios/Bookibra/ViewModels/HomeViewModel.swift | 4 +- .../Views/AddBooks/AddBooksView.swift | 349 +++++++--- .../Views/AddBooks/BarcodeScannerView.swift | 48 +- .../Views/Category/CategoryListView.swift | 140 ++-- ios/Bookibra/Views/Home/HomeView.swift | 4 +- 16 files changed, 976 insertions(+), 442 deletions(-) create mode 100644 ios/Bookibra/DesignSystem/Components/BookCardView.swift create mode 100644 ios/Bookibra/DesignSystem/Components/EmptyStateView.swift create mode 100644 ios/Bookibra/Models/ReadingStatus.swift diff --git a/ios/Bookibra.xcodeproj/project.pbxproj b/ios/Bookibra.xcodeproj/project.pbxproj index 3454e5d..5e19213 100644 --- a/ios/Bookibra.xcodeproj/project.pbxproj +++ b/ios/Bookibra.xcodeproj/project.pbxproj @@ -3,192 +3,201 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ - 0987C082DE634D36D5BF03DE /* ShelfSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C361617CEBCE704307DA0A9 /* ShelfSectionView.swift */; }; - 1DCD42AC02DDABACC54958C5 /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923CE0DF37333FFAF75668D5 /* UserProfile.swift */; }; - 245681EBBC7EB40F2733DD6B /* NetworkErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72DED90139ADCDD834AE33CF /* NetworkErrorView.swift */; }; - 2C2B30975D5DC09342690C43 /* BlurFogOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6F6AF589E6DD8E8EEF14CF /* BlurFogOverlay.swift */; }; - 31B3807F97E8E1512FB5DEEA /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15179D9ED03860747BBC180 /* CategoryViewModel.swift */; }; - 438812F1044DE6EBEA5B42E6 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72FE6CAAF6A00908AC19835F /* AVFoundation.framework */; }; - 44B8242D7211EDD650FCA488 /* BarcodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471C2927142735752EA378A9 /* BarcodeScannerView.swift */; }; - 4EE9C0CB6C3006780E8CDFA4 /* BookCoverCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349C2A1F39419A03C31114C3 /* BookCoverCard.swift */; }; - 5B73E67A2A3873F06000D8AF /* VisionKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1744A67EB267139F1EBDAE55 /* VisionKit.framework */; }; - 5DD75F144A0C411F2D7EF9C8 /* LibraryBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = B93B9F6AC3711B6E32030129 /* LibraryBook.swift */; }; - 6998E51506433C1B0A647330 /* BookibraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8155E61788F47524B11449 /* BookibraApp.swift */; }; - 6B60107AEE8C2489D19B1D9D /* mock_book_remote.json in Resources */ = {isa = PBXBuildFile; fileRef = 95A0AD7A2591540C4ED3252F /* mock_book_remote.json */; }; - 6E9DFC74E4EA2AC64A343E4C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0417E5217F2A37B2065F6DC9 /* Assets.xcassets */; }; - 7C130ABD8F4627EA3C0FB239 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2448E99A3D8CDBD5334901C7 /* ImageCache.swift */; }; - 7C17970A1EC6CE66AB2B6962 /* BookDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C4EA4FE11115BA86AB2802 /* BookDetailViewModel.swift */; }; - 7C5391EE19CD4370B0871A6F /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18379D6C3B16C83CCBCF22 /* HomeView.swift */; }; - 89E2012E58DEB2A6CA3191F9 /* AddBooksViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB571FD5EA0CB9C15DBD1DB /* AddBooksViewModel.swift */; }; - 90E97C917EEA4B7B1D09F8FB /* BookRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB98ADB94703A1CE74B79CD /* BookRemote.swift */; }; - 9DF5677130BA3D0F14C04B4B /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEB5F3DC625CEAE1BEC3911F /* HomeViewModel.swift */; }; - A08E9B72A9FAA96CC58C82B1 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 329388F7DE8EB288AEE98A23 /* AuthView.swift */; }; - A5BC1762D555E6DC13C8664E /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF5F3C9BA827C89A343166A4 /* AuthViewModel.swift */; }; - B404D223478123428719790C /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DF6CD123627625C1967D42 /* AppRouter.swift */; }; - B66DAE4BF97BCAC84740B541 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 20947201FBE7D30CD6F69E38 /* Localizable.strings */; }; - BDE45343461F02318DD86FDB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1B0A599E6FA80C6DBB852EB5 /* Localizable.strings */; }; - C9656D40284BF3D44322AE99 /* BookDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A39116C7E45F12244CA1DC23 /* BookDetailView.swift */; }; - CB31E95DBEF85410677B11E3 /* PrimaryPillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7B76B449982D19D91A67F3 /* PrimaryPillButton.swift */; }; - CD404352E3F12BCAE64E4BAC /* ScrewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DD9EB12817D19C6BC65306 /* ScrewView.swift */; }; - D146299A54D7A660951C3075 /* AddBooksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA01B4D0A49FA8E5D1E4F65 /* AddBooksView.swift */; }; - D2B224D07CFBA048947BCB22 /* SwiftData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 562B9464344AA711558F2AD0 /* SwiftData.framework */; }; - D6D4F249AA8A85A041C7D112 /* CategoryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D117FECFDDAC2EDDC191CEC9 /* CategoryListView.swift */; }; - E1E4040DBBACA7268F84998B /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 806F17D3E801BC96C6B5ED6D /* KeychainStore.swift */; }; - E7D483E62D94D20A511C6967 /* APIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B618484EE9FC15B0DCA5E055 /* APIClient.swift */; }; - EAA4D823C890C98F37F64E1E /* BooksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77D7EF1C78724F79956D5B1 /* BooksService.swift */; }; - EC793F00D7851722C0DD1633 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B226CA006E6EEA7DB04F100 /* Theme.swift */; }; - F3452503ADD5962494ABB38D /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAF98B022FA5BAA8F627DAAD /* AuthService.swift */; }; - F971998248197B0A0848FC88 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 192DFF277BADE93F9813BFFA /* Foundation.framework */; }; + 07D06DDC629661D80D4BB7A2 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27AEC6ED0A12CE62E5E7B42F /* CategoryViewModel.swift */; }; + 0E57D2E3B73CC64C45A13E5A /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CC67809D9F26DFB7F53280 /* AuthViewModel.swift */; }; + 1569FBE19975206A58F8E694 /* ReadingStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94DACFF2F104F68F5E72793 /* ReadingStatus.swift */; }; + 17B5B7FBC0025E80FA65036F /* ScrewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091D8A6C89ACBCDD9D022FB4 /* ScrewView.swift */; }; + 1B120941F76A7C9ACC9B82C2 /* CategoryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4C72C39F36045BF4D5ECAF /* CategoryListView.swift */; }; + 256166E4812B7DF17BD32FD4 /* VisionKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DDA87F942C70D1FE96ED9C4 /* VisionKit.framework */; }; + 3DEF8AE33F0E47F9E942FAD6 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C0E4FA7E8B3F62D198485B /* AuthView.swift */; }; + 4FC8807E8263C91ADA8FA591 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479C2FCDB84B962FF4BF68AE /* ImageCache.swift */; }; + 546CB89AAC170EE1B541830D /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC26B431A2E4633F2FC89CA /* HomeView.swift */; }; + 686D28482BE40BE453EFE0D3 /* mock_book_remote.json in Resources */ = {isa = PBXBuildFile; fileRef = 0E5A7ADF9962345CB78AC571 /* mock_book_remote.json */; }; + 69CAD3618DD77D79F462C76E /* SwiftData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87EF451B4A1589BBCB00D7F8 /* SwiftData.framework */; }; + 6E060ACBDE0AD85FDB2F8010 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC488BF2DA3B6B94C2958E8 /* AuthService.swift */; }; + 6F398B380F29EF5C2C6E1653 /* APIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDA9B5DE02839213D93F6A9 /* APIClient.swift */; }; + 6F59713411606CDC297CF733 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EABC698DDB8062029E5E9FA /* Foundation.framework */; }; + 72212EAE36C9151956F84262 /* ShelfSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0887E6542AB83E17C9490AF /* ShelfSectionView.swift */; }; + 76CDBAECDA13604B0A3EC338 /* BookDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523E0F614C3E08527D265ADC /* BookDetailView.swift */; }; + 7FC47B691C19F4E6C1102606 /* BookCoverCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0C3AF58827D6FBF93157B7 /* BookCoverCard.swift */; }; + 8287AA6A6111C8E14BA92E81 /* BarcodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BEE917DB0A13B5EA34D030 /* BarcodeScannerView.swift */; }; + 865A3EBCDA84FBA692A31939 /* EmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A850CCC78FC41E21559CE03A /* EmptyStateView.swift */; }; + 8707C5BBD8347B96B1E4DFB8 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0563C287DF8A317E7F6D4A22 /* HomeViewModel.swift */; }; + 919B2721944E5A9DF8F696AF /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7DCABD0AFF317C4C25C1D3 /* KeychainStore.swift */; }; + 9296F2BCE6EB7FFAC4F6A5B1 /* NetworkErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026D1C09514A01EB0D4C7641 /* NetworkErrorView.swift */; }; + 971C5D3C6576578B4B4B8CBB /* BooksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F2AEEF3846E89EF58E8E2B /* BooksService.swift */; }; + 97FD0730276CEBBE6BDBCCA1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B3CA8360A84C636945C83DD0 /* Localizable.strings */; }; + 9C71E8718C4820EFD2F50C17 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0A47804190A193B8EE3159EA /* Assets.xcassets */; }; + A597F6A4E13688A21BD3FEAE /* LibraryBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB981066E192B332E6BB5E2 /* LibraryBook.swift */; }; + A6E600A345801C9AF1CB1F10 /* AddBooksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E960D3986FCFF358D1CC83 /* AddBooksView.swift */; }; + AE16C52E1D5A765D0723F243 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC4F542A93E316D86251043 /* AppRouter.swift */; }; + B1E7E3F062B3B01ED5EB7019 /* BlurFogOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3448927BF4293DFAF113048 /* BlurFogOverlay.swift */; }; + B37F57B2BB478E72702A551E /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9547DCE4663825EA1267F3C /* UserProfile.swift */; }; + C59A615F50B6A23903E02280 /* BookCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 216DFB59A593E4AD349FCE0F /* BookCardView.swift */; }; + CE6FC5E5744AA2A05C3B6049 /* BookRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3D6631E1FD7160883FC31F4 /* BookRemote.swift */; }; + D9D5AD82B32F16BD92E82238 /* PrimaryPillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D60CE4B6FDA59B1114B8168 /* PrimaryPillButton.swift */; }; + DC0602EC768B6265564FE8FD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 395E4C35E6D3C362B550AF3B /* Localizable.strings */; }; + E0FC3411B06A5726172021BE /* AddBooksViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AD40B111A582310571F11E /* AddBooksViewModel.swift */; }; + E318F3175C8B01CEA69F50C9 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C4E14D093F318ECAB9FA6A /* Theme.swift */; }; + EB4366DC44468E12BCE5177D /* BookibraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F25BA817C2459E1751520C /* BookibraApp.swift */; }; + ECDD56903097A589E63C7D64 /* BookDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E9BD1CDD8189A9BD8F93C0 /* BookDetailViewModel.swift */; }; + F3E86067A1062297B95CE2CA /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF373FA01D444E33C45D1B74 /* AVFoundation.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0417E5217F2A37B2065F6DC9 /* Assets.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Resources/Assets.xcassets; sourceTree = ""; }; - 1744A67EB267139F1EBDAE55 /* VisionKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VisionKit.framework; path = System/Library/Frameworks/VisionKit.framework; sourceTree = ""; }; - 192DFF277BADE93F9813BFFA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 1B0A599E6FA80C6DBB852EB5 /* Localizable.strings */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = Resources/tr.lproj/Localizable.strings; sourceTree = ""; }; - 1EB98ADB94703A1CE74B79CD /* BookRemote.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookRemote.swift; path = Models/BookRemote.swift; sourceTree = ""; }; - 20947201FBE7D30CD6F69E38 /* Localizable.strings */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = Resources/en.lproj/Localizable.strings; sourceTree = ""; }; - 2448E99A3D8CDBD5334901C7 /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Services/ImageCache.swift; sourceTree = ""; }; - 2F8155E61788F47524B11449 /* BookibraApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookibraApp.swift; path = App/BookibraApp.swift; sourceTree = ""; }; - 329388F7DE8EB288AEE98A23 /* AuthView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthView.swift; path = Views/Auth/AuthView.swift; sourceTree = ""; }; - 349C2A1F39419A03C31114C3 /* BookCoverCard.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookCoverCard.swift; path = DesignSystem/Components/BookCoverCard.swift; sourceTree = ""; }; - 3B226CA006E6EEA7DB04F100 /* Theme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = DesignSystem/Theme.swift; sourceTree = ""; }; - 3BB571FD5EA0CB9C15DBD1DB /* AddBooksViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AddBooksViewModel.swift; path = ViewModels/AddBooksViewModel.swift; sourceTree = ""; }; - 471C2927142735752EA378A9 /* BarcodeScannerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarcodeScannerView.swift; path = Views/AddBooks/BarcodeScannerView.swift; sourceTree = ""; }; - 4C361617CEBCE704307DA0A9 /* ShelfSectionView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShelfSectionView.swift; path = DesignSystem/Components/ShelfSectionView.swift; sourceTree = ""; }; - 4DA01B4D0A49FA8E5D1E4F65 /* AddBooksView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AddBooksView.swift; path = Views/AddBooks/AddBooksView.swift; sourceTree = ""; }; - 562B9464344AA711558F2AD0 /* SwiftData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftData.framework; path = System/Library/Frameworks/SwiftData.framework; sourceTree = ""; }; - 72DED90139ADCDD834AE33CF /* NetworkErrorView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkErrorView.swift; path = DesignSystem/Components/NetworkErrorView.swift; sourceTree = ""; }; - 72FE6CAAF6A00908AC19835F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = ""; }; - 7B6C1F4EB35DF0216BC86061 /* Bookibra.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bookibra.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 806F17D3E801BC96C6B5ED6D /* KeychainStore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeychainStore.swift; path = Services/KeychainStore.swift; sourceTree = ""; }; - 923CE0DF37333FFAF75668D5 /* UserProfile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UserProfile.swift; path = Models/UserProfile.swift; sourceTree = ""; }; - 95A0AD7A2591540C4ED3252F /* mock_book_remote.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = mock_book_remote.json; path = Resources/mock_book_remote.json; sourceTree = ""; }; - 96C4EA4FE11115BA86AB2802 /* BookDetailViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookDetailViewModel.swift; path = ViewModels/BookDetailViewModel.swift; sourceTree = ""; }; - 98AAF8E8F7F53CA9CE81186B /* Release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Bookibra/Resources/Release.xcconfig; sourceTree = ""; }; - A39116C7E45F12244CA1DC23 /* BookDetailView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookDetailView.swift; path = Views/Detail/BookDetailView.swift; sourceTree = ""; }; - A77D7EF1C78724F79956D5B1 /* BooksService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BooksService.swift; path = Services/BooksService.swift; sourceTree = ""; }; - AF5F3C9BA827C89A343166A4 /* AuthViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthViewModel.swift; path = ViewModels/AuthViewModel.swift; sourceTree = ""; }; - AFD5A6DD3D2BFC014B9AB859 /* Debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Bookibra/Resources/Debug.xcconfig; sourceTree = ""; }; - B618484EE9FC15B0DCA5E055 /* APIClient.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = APIClient.swift; path = Services/APIClient.swift; sourceTree = ""; }; - B93B9F6AC3711B6E32030129 /* LibraryBook.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LibraryBook.swift; path = Models/LibraryBook.swift; sourceTree = ""; }; - BEB5F3DC625CEAE1BEC3911F /* HomeViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeViewModel.swift; path = ViewModels/HomeViewModel.swift; sourceTree = ""; }; - BF18379D6C3B16C83CCBCF22 /* HomeView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeView.swift; path = Views/Home/HomeView.swift; sourceTree = ""; }; - C15179D9ED03860747BBC180 /* CategoryViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CategoryViewModel.swift; path = ViewModels/CategoryViewModel.swift; sourceTree = ""; }; - C8DD9EB12817D19C6BC65306 /* ScrewView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScrewView.swift; path = DesignSystem/Components/ScrewView.swift; sourceTree = ""; }; - CAF98B022FA5BAA8F627DAAD /* AuthService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthService.swift; path = Services/AuthService.swift; sourceTree = ""; }; - D117FECFDDAC2EDDC191CEC9 /* CategoryListView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CategoryListView.swift; path = Views/Category/CategoryListView.swift; sourceTree = ""; }; - DC6F6AF589E6DD8E8EEF14CF /* BlurFogOverlay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlurFogOverlay.swift; path = DesignSystem/Components/BlurFogOverlay.swift; sourceTree = ""; }; - E4DF6CD123627625C1967D42 /* AppRouter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AppRouter.swift; path = App/AppRouter.swift; sourceTree = ""; }; - FE7B76B449982D19D91A67F3 /* PrimaryPillButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PrimaryPillButton.swift; path = DesignSystem/Components/PrimaryPillButton.swift; sourceTree = ""; }; + 026D1C09514A01EB0D4C7641 /* NetworkErrorView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkErrorView.swift; path = DesignSystem/Components/NetworkErrorView.swift; sourceTree = ""; }; + 0563C287DF8A317E7F6D4A22 /* HomeViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeViewModel.swift; path = ViewModels/HomeViewModel.swift; sourceTree = ""; }; + 06BEE917DB0A13B5EA34D030 /* BarcodeScannerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarcodeScannerView.swift; path = Views/AddBooks/BarcodeScannerView.swift; sourceTree = ""; }; + 091D8A6C89ACBCDD9D022FB4 /* ScrewView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScrewView.swift; path = DesignSystem/Components/ScrewView.swift; sourceTree = ""; }; + 09E9BD1CDD8189A9BD8F93C0 /* BookDetailViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookDetailViewModel.swift; path = ViewModels/BookDetailViewModel.swift; sourceTree = ""; }; + 0A47804190A193B8EE3159EA /* Assets.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Resources/Assets.xcassets; sourceTree = ""; }; + 0E5A7ADF9962345CB78AC571 /* mock_book_remote.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = mock_book_remote.json; path = Resources/mock_book_remote.json; sourceTree = ""; }; + 12C0E4FA7E8B3F62D198485B /* AuthView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthView.swift; path = Views/Auth/AuthView.swift; sourceTree = ""; }; + 216DFB59A593E4AD349FCE0F /* BookCardView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookCardView.swift; path = DesignSystem/Components/BookCardView.swift; sourceTree = ""; }; + 27AEC6ED0A12CE62E5E7B42F /* CategoryViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CategoryViewModel.swift; path = ViewModels/CategoryViewModel.swift; sourceTree = ""; }; + 2D4C72C39F36045BF4D5ECAF /* CategoryListView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CategoryListView.swift; path = Views/Category/CategoryListView.swift; sourceTree = ""; }; + 395E4C35E6D3C362B550AF3B /* Localizable.strings */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = Resources/tr.lproj/Localizable.strings; sourceTree = ""; }; + 3BC26B431A2E4633F2FC89CA /* HomeView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeView.swift; path = Views/Home/HomeView.swift; sourceTree = ""; }; + 3D60CE4B6FDA59B1114B8168 /* PrimaryPillButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PrimaryPillButton.swift; path = DesignSystem/Components/PrimaryPillButton.swift; sourceTree = ""; }; + 3DDA87F942C70D1FE96ED9C4 /* VisionKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VisionKit.framework; path = System/Library/Frameworks/VisionKit.framework; sourceTree = ""; }; + 40AD40B111A582310571F11E /* AddBooksViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AddBooksViewModel.swift; path = ViewModels/AddBooksViewModel.swift; sourceTree = ""; }; + 46E960D3986FCFF358D1CC83 /* AddBooksView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AddBooksView.swift; path = Views/AddBooks/AddBooksView.swift; sourceTree = ""; }; + 479C2FCDB84B962FF4BF68AE /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Services/ImageCache.swift; sourceTree = ""; }; + 4AC4F542A93E316D86251043 /* AppRouter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AppRouter.swift; path = App/AppRouter.swift; sourceTree = ""; }; + 4E0C3AF58827D6FBF93157B7 /* BookCoverCard.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookCoverCard.swift; path = DesignSystem/Components/BookCoverCard.swift; sourceTree = ""; }; + 4EDA9B5DE02839213D93F6A9 /* APIClient.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = APIClient.swift; path = Services/APIClient.swift; sourceTree = ""; }; + 523E0F614C3E08527D265ADC /* BookDetailView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookDetailView.swift; path = Views/Detail/BookDetailView.swift; sourceTree = ""; }; + 5EABC698DDB8062029E5E9FA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 67C4E14D093F318ECAB9FA6A /* Theme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = DesignSystem/Theme.swift; sourceTree = ""; }; + 86CC67809D9F26DFB7F53280 /* AuthViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthViewModel.swift; path = ViewModels/AuthViewModel.swift; sourceTree = ""; }; + 87EF451B4A1589BBCB00D7F8 /* SwiftData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftData.framework; path = System/Library/Frameworks/SwiftData.framework; sourceTree = ""; }; + 9D96BF3D1EBDBB9B18FE826D /* Bookibra.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bookibra.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A850CCC78FC41E21559CE03A /* EmptyStateView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EmptyStateView.swift; path = DesignSystem/Components/EmptyStateView.swift; sourceTree = ""; }; + A8F25BA817C2459E1751520C /* BookibraApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookibraApp.swift; path = App/BookibraApp.swift; sourceTree = ""; }; + B3448927BF4293DFAF113048 /* BlurFogOverlay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlurFogOverlay.swift; path = DesignSystem/Components/BlurFogOverlay.swift; sourceTree = ""; }; + B3CA8360A84C636945C83DD0 /* Localizable.strings */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = Resources/en.lproj/Localizable.strings; sourceTree = ""; }; + B44591FBA4BB2AEA612510DD /* Debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Bookibra/Resources/Debug.xcconfig; sourceTree = ""; }; + C0887E6542AB83E17C9490AF /* ShelfSectionView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShelfSectionView.swift; path = DesignSystem/Components/ShelfSectionView.swift; sourceTree = ""; }; + C94DACFF2F104F68F5E72793 /* ReadingStatus.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReadingStatus.swift; path = Models/ReadingStatus.swift; sourceTree = ""; }; + CAC488BF2DA3B6B94C2958E8 /* AuthService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AuthService.swift; path = Services/AuthService.swift; sourceTree = ""; }; + D1F2AEEF3846E89EF58E8E2B /* BooksService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BooksService.swift; path = Services/BooksService.swift; sourceTree = ""; }; + D9547DCE4663825EA1267F3C /* UserProfile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UserProfile.swift; path = Models/UserProfile.swift; sourceTree = ""; }; + DF373FA01D444E33C45D1B74 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = ""; }; + E3D6631E1FD7160883FC31F4 /* BookRemote.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BookRemote.swift; path = Models/BookRemote.swift; sourceTree = ""; }; + FDC0CE87CC54E1384BD6557B /* Release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Bookibra/Resources/Release.xcconfig; sourceTree = ""; }; + FF7DCABD0AFF317C4C25C1D3 /* KeychainStore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeychainStore.swift; path = Services/KeychainStore.swift; sourceTree = ""; }; + FFB981066E192B332E6BB5E2 /* LibraryBook.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LibraryBook.swift; path = Models/LibraryBook.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 96BCE7EBCFE3A1B03D95C0A5 /* Frameworks */ = { + E88BA262CD3F12CEBF38184A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F971998248197B0A0848FC88 /* Foundation.framework in Frameworks */, - 438812F1044DE6EBEA5B42E6 /* AVFoundation.framework in Frameworks */, - 5B73E67A2A3873F06000D8AF /* VisionKit.framework in Frameworks */, - D2B224D07CFBA048947BCB22 /* SwiftData.framework in Frameworks */, + 6F59713411606CDC297CF733 /* Foundation.framework in Frameworks */, + F3E86067A1062297B95CE2CA /* AVFoundation.framework in Frameworks */, + 256166E4812B7DF17BD32FD4 /* VisionKit.framework in Frameworks */, + 69CAD3618DD77D79F462C76E /* SwiftData.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 11824F4150C100F76F38DBD8 = { + 1F97BBDD9672559E121C4FFA /* Frameworks */ = { isa = PBXGroup; children = ( - 94F7452CA97CED98C86AF84E /* Products */, - 8EB4884498D4ACBA5BB04312 /* Frameworks */, - AFD5A6DD3D2BFC014B9AB859 /* Debug.xcconfig */, - 98AAF8E8F7F53CA9CE81186B /* Release.xcconfig */, - F2E80CFCFD2FE3BF793C4147 /* Bookibra */, - ); - sourceTree = ""; - }; - 8EB4884498D4ACBA5BB04312 /* Frameworks */ = { - isa = PBXGroup; - children = ( - E592B0F1D9DDB4B2033A4A3D /* iOS */, - 72FE6CAAF6A00908AC19835F /* AVFoundation.framework */, - 1744A67EB267139F1EBDAE55 /* VisionKit.framework */, - 562B9464344AA711558F2AD0 /* SwiftData.framework */, + 71736683D1BB2B9A0FF730B9 /* iOS */, + DF373FA01D444E33C45D1B74 /* AVFoundation.framework */, + 3DDA87F942C70D1FE96ED9C4 /* VisionKit.framework */, + 87EF451B4A1589BBCB00D7F8 /* SwiftData.framework */, ); name = Frameworks; sourceTree = ""; }; - 94F7452CA97CED98C86AF84E /* Products */ = { + 3D7744C35B489BFB545D42AD /* Products */ = { isa = PBXGroup; children = ( - 7B6C1F4EB35DF0216BC86061 /* Bookibra.app */, + 9D96BF3D1EBDBB9B18FE826D /* Bookibra.app */, ); name = Products; sourceTree = ""; }; - E592B0F1D9DDB4B2033A4A3D /* iOS */ = { + 41E096AA37EBB337950112DD /* Bookibra */ = { isa = PBXGroup; children = ( - 192DFF277BADE93F9813BFFA /* Foundation.framework */, + 4AC4F542A93E316D86251043 /* AppRouter.swift */, + A8F25BA817C2459E1751520C /* BookibraApp.swift */, + B3448927BF4293DFAF113048 /* BlurFogOverlay.swift */, + 216DFB59A593E4AD349FCE0F /* BookCardView.swift */, + 4E0C3AF58827D6FBF93157B7 /* BookCoverCard.swift */, + A850CCC78FC41E21559CE03A /* EmptyStateView.swift */, + 026D1C09514A01EB0D4C7641 /* NetworkErrorView.swift */, + 3D60CE4B6FDA59B1114B8168 /* PrimaryPillButton.swift */, + 091D8A6C89ACBCDD9D022FB4 /* ScrewView.swift */, + C0887E6542AB83E17C9490AF /* ShelfSectionView.swift */, + 67C4E14D093F318ECAB9FA6A /* Theme.swift */, + E3D6631E1FD7160883FC31F4 /* BookRemote.swift */, + FFB981066E192B332E6BB5E2 /* LibraryBook.swift */, + C94DACFF2F104F68F5E72793 /* ReadingStatus.swift */, + D9547DCE4663825EA1267F3C /* UserProfile.swift */, + 4EDA9B5DE02839213D93F6A9 /* APIClient.swift */, + CAC488BF2DA3B6B94C2958E8 /* AuthService.swift */, + D1F2AEEF3846E89EF58E8E2B /* BooksService.swift */, + 479C2FCDB84B962FF4BF68AE /* ImageCache.swift */, + FF7DCABD0AFF317C4C25C1D3 /* KeychainStore.swift */, + 40AD40B111A582310571F11E /* AddBooksViewModel.swift */, + 86CC67809D9F26DFB7F53280 /* AuthViewModel.swift */, + 09E9BD1CDD8189A9BD8F93C0 /* BookDetailViewModel.swift */, + 27AEC6ED0A12CE62E5E7B42F /* CategoryViewModel.swift */, + 0563C287DF8A317E7F6D4A22 /* HomeViewModel.swift */, + 46E960D3986FCFF358D1CC83 /* AddBooksView.swift */, + 06BEE917DB0A13B5EA34D030 /* BarcodeScannerView.swift */, + 12C0E4FA7E8B3F62D198485B /* AuthView.swift */, + 2D4C72C39F36045BF4D5ECAF /* CategoryListView.swift */, + 523E0F614C3E08527D265ADC /* BookDetailView.swift */, + 3BC26B431A2E4633F2FC89CA /* HomeView.swift */, + 0A47804190A193B8EE3159EA /* Assets.xcassets */, + B3CA8360A84C636945C83DD0 /* Localizable.strings */, + 395E4C35E6D3C362B550AF3B /* Localizable.strings */, + 0E5A7ADF9962345CB78AC571 /* mock_book_remote.json */, + ); + path = Bookibra; + sourceTree = ""; + }; + 71736683D1BB2B9A0FF730B9 /* iOS */ = { + isa = PBXGroup; + children = ( + 5EABC698DDB8062029E5E9FA /* Foundation.framework */, ); name = iOS; sourceTree = ""; }; - F2E80CFCFD2FE3BF793C4147 /* Bookibra */ = { + FC7D15935DED2E50DD94B7D5 = { isa = PBXGroup; children = ( - E4DF6CD123627625C1967D42 /* AppRouter.swift */, - 2F8155E61788F47524B11449 /* BookibraApp.swift */, - DC6F6AF589E6DD8E8EEF14CF /* BlurFogOverlay.swift */, - 349C2A1F39419A03C31114C3 /* BookCoverCard.swift */, - 72DED90139ADCDD834AE33CF /* NetworkErrorView.swift */, - FE7B76B449982D19D91A67F3 /* PrimaryPillButton.swift */, - C8DD9EB12817D19C6BC65306 /* ScrewView.swift */, - 4C361617CEBCE704307DA0A9 /* ShelfSectionView.swift */, - 3B226CA006E6EEA7DB04F100 /* Theme.swift */, - 1EB98ADB94703A1CE74B79CD /* BookRemote.swift */, - B93B9F6AC3711B6E32030129 /* LibraryBook.swift */, - 923CE0DF37333FFAF75668D5 /* UserProfile.swift */, - B618484EE9FC15B0DCA5E055 /* APIClient.swift */, - CAF98B022FA5BAA8F627DAAD /* AuthService.swift */, - A77D7EF1C78724F79956D5B1 /* BooksService.swift */, - 2448E99A3D8CDBD5334901C7 /* ImageCache.swift */, - 806F17D3E801BC96C6B5ED6D /* KeychainStore.swift */, - 3BB571FD5EA0CB9C15DBD1DB /* AddBooksViewModel.swift */, - AF5F3C9BA827C89A343166A4 /* AuthViewModel.swift */, - 96C4EA4FE11115BA86AB2802 /* BookDetailViewModel.swift */, - C15179D9ED03860747BBC180 /* CategoryViewModel.swift */, - BEB5F3DC625CEAE1BEC3911F /* HomeViewModel.swift */, - 4DA01B4D0A49FA8E5D1E4F65 /* AddBooksView.swift */, - 471C2927142735752EA378A9 /* BarcodeScannerView.swift */, - 329388F7DE8EB288AEE98A23 /* AuthView.swift */, - D117FECFDDAC2EDDC191CEC9 /* CategoryListView.swift */, - A39116C7E45F12244CA1DC23 /* BookDetailView.swift */, - BF18379D6C3B16C83CCBCF22 /* HomeView.swift */, - 0417E5217F2A37B2065F6DC9 /* Assets.xcassets */, - 20947201FBE7D30CD6F69E38 /* Localizable.strings */, - 1B0A599E6FA80C6DBB852EB5 /* Localizable.strings */, - 95A0AD7A2591540C4ED3252F /* mock_book_remote.json */, + 3D7744C35B489BFB545D42AD /* Products */, + 1F97BBDD9672559E121C4FFA /* Frameworks */, + B44591FBA4BB2AEA612510DD /* Debug.xcconfig */, + FDC0CE87CC54E1384BD6557B /* Release.xcconfig */, + 41E096AA37EBB337950112DD /* Bookibra */, ); - path = Bookibra; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - A0CA764B5F4498B2368FB4C7 /* Bookibra */ = { + 0E74DD6C4F903703E3E4BCAD /* Bookibra */ = { isa = PBXNativeTarget; - buildConfigurationList = 0D8C0A6CBE24CFFE953CC286 /* Build configuration list for PBXNativeTarget "Bookibra" */; + buildConfigurationList = 27C2348C30A81863879653CA /* Build configuration list for PBXNativeTarget "Bookibra" */; buildPhases = ( - 4B0B88378B618BDE3340051C /* Sources */, - 96BCE7EBCFE3A1B03D95C0A5 /* Frameworks */, - AD49F7B39587DC1F8AC10D9B /* Resources */, + D93C53F09F67A433B3053B5D /* Sources */, + E88BA262CD3F12CEBF38184A /* Frameworks */, + 4ED22AFE6C5FD2EAFD12AE94 /* Resources */, ); buildRules = ( ); @@ -196,25 +205,20 @@ ); name = Bookibra; productName = Bookibra; - productReference = 7B6C1F4EB35DF0216BC86061 /* Bookibra.app */; + productReference = 9D96BF3D1EBDBB9B18FE826D /* Bookibra.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 1229A2BA11F02DDB82E39253 /* Project object */ = { + 4A57E4BC2562874685F10390 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1600; - LastUpgradeCheck = 1600; - TargetAttributes = { - A0CA764B5F4498B2368FB4C7 = { - DevelopmentTeam = S34SFUY9SC; - ProvisioningStyle = Automatic; - }; - }; + LastUpgradeCheck = 2620; }; - buildConfigurationList = AAD88BDD3442AC750E1C238E /* Build configuration list for PBXProject "Bookibra" */; + buildConfigurationList = 66162088EAD405EAB915FA6C /* Build configuration list for PBXProject "Bookibra" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; @@ -222,92 +226,75 @@ en, Base, ); - mainGroup = 11824F4150C100F76F38DBD8; - productRefGroup = 94F7452CA97CED98C86AF84E /* Products */; + mainGroup = FC7D15935DED2E50DD94B7D5; + productRefGroup = 3D7744C35B489BFB545D42AD /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - A0CA764B5F4498B2368FB4C7 /* Bookibra */, + 0E74DD6C4F903703E3E4BCAD /* Bookibra */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - AD49F7B39587DC1F8AC10D9B /* Resources */ = { + 4ED22AFE6C5FD2EAFD12AE94 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6E9DFC74E4EA2AC64A343E4C /* Assets.xcassets in Resources */, - B66DAE4BF97BCAC84740B541 /* Localizable.strings in Resources */, - BDE45343461F02318DD86FDB /* Localizable.strings in Resources */, - 6B60107AEE8C2489D19B1D9D /* mock_book_remote.json in Resources */, + 9C71E8718C4820EFD2F50C17 /* Assets.xcassets in Resources */, + 97FD0730276CEBBE6BDBCCA1 /* Localizable.strings in Resources */, + DC0602EC768B6265564FE8FD /* Localizable.strings in Resources */, + 686D28482BE40BE453EFE0D3 /* mock_book_remote.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 4B0B88378B618BDE3340051C /* Sources */ = { + D93C53F09F67A433B3053B5D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B404D223478123428719790C /* AppRouter.swift in Sources */, - 6998E51506433C1B0A647330 /* BookibraApp.swift in Sources */, - 2C2B30975D5DC09342690C43 /* BlurFogOverlay.swift in Sources */, - 4EE9C0CB6C3006780E8CDFA4 /* BookCoverCard.swift in Sources */, - 245681EBBC7EB40F2733DD6B /* NetworkErrorView.swift in Sources */, - CB31E95DBEF85410677B11E3 /* PrimaryPillButton.swift in Sources */, - CD404352E3F12BCAE64E4BAC /* ScrewView.swift in Sources */, - 0987C082DE634D36D5BF03DE /* ShelfSectionView.swift in Sources */, - EC793F00D7851722C0DD1633 /* Theme.swift in Sources */, - 90E97C917EEA4B7B1D09F8FB /* BookRemote.swift in Sources */, - 5DD75F144A0C411F2D7EF9C8 /* LibraryBook.swift in Sources */, - 1DCD42AC02DDABACC54958C5 /* UserProfile.swift in Sources */, - E7D483E62D94D20A511C6967 /* APIClient.swift in Sources */, - F3452503ADD5962494ABB38D /* AuthService.swift in Sources */, - EAA4D823C890C98F37F64E1E /* BooksService.swift in Sources */, - 7C130ABD8F4627EA3C0FB239 /* ImageCache.swift in Sources */, - E1E4040DBBACA7268F84998B /* KeychainStore.swift in Sources */, - 89E2012E58DEB2A6CA3191F9 /* AddBooksViewModel.swift in Sources */, - A5BC1762D555E6DC13C8664E /* AuthViewModel.swift in Sources */, - 7C17970A1EC6CE66AB2B6962 /* BookDetailViewModel.swift in Sources */, - 31B3807F97E8E1512FB5DEEA /* CategoryViewModel.swift in Sources */, - 9DF5677130BA3D0F14C04B4B /* HomeViewModel.swift in Sources */, - D146299A54D7A660951C3075 /* AddBooksView.swift in Sources */, - 44B8242D7211EDD650FCA488 /* BarcodeScannerView.swift in Sources */, - A08E9B72A9FAA96CC58C82B1 /* AuthView.swift in Sources */, - D6D4F249AA8A85A041C7D112 /* CategoryListView.swift in Sources */, - C9656D40284BF3D44322AE99 /* BookDetailView.swift in Sources */, - 7C5391EE19CD4370B0871A6F /* HomeView.swift in Sources */, + AE16C52E1D5A765D0723F243 /* AppRouter.swift in Sources */, + EB4366DC44468E12BCE5177D /* BookibraApp.swift in Sources */, + B1E7E3F062B3B01ED5EB7019 /* BlurFogOverlay.swift in Sources */, + C59A615F50B6A23903E02280 /* BookCardView.swift in Sources */, + 7FC47B691C19F4E6C1102606 /* BookCoverCard.swift in Sources */, + 865A3EBCDA84FBA692A31939 /* EmptyStateView.swift in Sources */, + 9296F2BCE6EB7FFAC4F6A5B1 /* NetworkErrorView.swift in Sources */, + D9D5AD82B32F16BD92E82238 /* PrimaryPillButton.swift in Sources */, + 17B5B7FBC0025E80FA65036F /* ScrewView.swift in Sources */, + 72212EAE36C9151956F84262 /* ShelfSectionView.swift in Sources */, + E318F3175C8B01CEA69F50C9 /* Theme.swift in Sources */, + CE6FC5E5744AA2A05C3B6049 /* BookRemote.swift in Sources */, + A597F6A4E13688A21BD3FEAE /* LibraryBook.swift in Sources */, + 1569FBE19975206A58F8E694 /* ReadingStatus.swift in Sources */, + B37F57B2BB478E72702A551E /* UserProfile.swift in Sources */, + 6F398B380F29EF5C2C6E1653 /* APIClient.swift in Sources */, + 6E060ACBDE0AD85FDB2F8010 /* AuthService.swift in Sources */, + 971C5D3C6576578B4B4B8CBB /* BooksService.swift in Sources */, + 4FC8807E8263C91ADA8FA591 /* ImageCache.swift in Sources */, + 919B2721944E5A9DF8F696AF /* KeychainStore.swift in Sources */, + E0FC3411B06A5726172021BE /* AddBooksViewModel.swift in Sources */, + 0E57D2E3B73CC64C45A13E5A /* AuthViewModel.swift in Sources */, + ECDD56903097A589E63C7D64 /* BookDetailViewModel.swift in Sources */, + 07D06DDC629661D80D4BB7A2 /* CategoryViewModel.swift in Sources */, + 8707C5BBD8347B96B1E4DFB8 /* HomeViewModel.swift in Sources */, + A6E600A345801C9AF1CB1F10 /* AddBooksView.swift in Sources */, + 8287AA6A6111C8E14BA92E81 /* BarcodeScannerView.swift in Sources */, + 3DEF8AE33F0E47F9E942FAD6 /* AuthView.swift in Sources */, + 1B120941F76A7C9ACC9B82C2 /* CategoryListView.swift in Sources */, + 76CDBAECDA13604B0A3EC338 /* BookDetailView.swift in Sources */, + 546CB89AAC170EE1B541830D /* HomeView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - 180A253868383C3109315D0F /* Debug */ = { + 53E3CF14C3FC84D36F571AEE /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AFD5A6DD3D2BFC014B9AB859 /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - DEVELOPMENT_TEAM = S34SFUY9SC; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = Bookibra/Resources/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.9; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 2D538BCF427EE4B9D90BA1E5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AFD5A6DD3D2BFC014B9AB859 /* Debug.xcconfig */; + baseConfigurationReference = FDC0CE87CC54E1384BD6557B /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -342,9 +329,120 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.9; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 883CA3D20F5C26D151A2C437 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FDC0CE87CC54E1384BD6557B /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Bookibra/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.9; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CDAF5161EEF6E3EE85E10530 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B44591FBA4BB2AEA612510DD /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Bookibra/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.9; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F6310A668DF5F079EAD0B12C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B44591FBA4BB2AEA612510DD /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -366,6 +464,7 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.9; @@ -373,109 +472,28 @@ }; name = Debug; }; - 4BC6D305A4C5E17883964793 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 98AAF8E8F7F53CA9CE81186B /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.9; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - EE4DDF7AD06BABD0D78E43A0 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 98AAF8E8F7F53CA9CE81186B /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - DEVELOPMENT_TEAM = S34SFUY9SC; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = Bookibra/Resources/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.bookibra.ios; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.9; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 0D8C0A6CBE24CFFE953CC286 /* Build configuration list for PBXNativeTarget "Bookibra" */ = { + 27C2348C30A81863879653CA /* Build configuration list for PBXNativeTarget "Bookibra" */ = { isa = XCConfigurationList; buildConfigurations = ( - EE4DDF7AD06BABD0D78E43A0 /* Release */, - 180A253868383C3109315D0F /* Debug */, + 883CA3D20F5C26D151A2C437 /* Release */, + CDAF5161EEF6E3EE85E10530 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - AAD88BDD3442AC750E1C238E /* Build configuration list for PBXProject "Bookibra" */ = { + 66162088EAD405EAB915FA6C /* Build configuration list for PBXProject "Bookibra" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2D538BCF427EE4B9D90BA1E5 /* Debug */, - 4BC6D305A4C5E17883964793 /* Release */, + F6310A668DF5F079EAD0B12C /* Debug */, + 53E3CF14C3FC84D36F571AEE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 1229A2BA11F02DDB82E39253 /* Project object */; + rootObject = 4A57E4BC2562874685F10390 /* Project object */; } diff --git a/ios/Bookibra.xcodeproj/project.xcworkspace/xcuserdata/wisecolt.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Bookibra.xcodeproj/project.xcworkspace/xcuserdata/wisecolt.xcuserdatad/UserInterfaceState.xcuserstate index 1cbe98979c387719c3ccc44a2e8806408873a9a2..b5832bdad4865fd796978b95dea3014285043bda 100644 GIT binary patch delta 12487 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 Void)? + + var body: some View { + VStack(spacing: Theme.Spacing.medium) { + Image(systemName: symbol) + .font(.system(size: 44, weight: .semibold)) + .foregroundStyle(.secondary) + + Text(title) + .font(.title3.weight(.semibold)) + + Text(message) + .font(.body) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + + if let buttonTitle, let action { + Button(buttonTitle, action: action) + .buttonStyle(.borderedProminent) + } + } + .padding(24) + .frame(maxWidth: .infinity) + .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + .padding(.horizontal, 20) + } +} diff --git a/ios/Bookibra/DesignSystem/Theme.swift b/ios/Bookibra/DesignSystem/Theme.swift index 8c2c741..2154366 100644 --- a/ios/Bookibra/DesignSystem/Theme.swift +++ b/ios/Bookibra/DesignSystem/Theme.swift @@ -26,4 +26,21 @@ enum Theme { static func headerSerif(size: CGFloat) -> Font { .custom("NewYork-Regular", size: size, relativeTo: .largeTitle) } + + enum Spacing { + static let xSmall: CGFloat = 6 + static let small: CGFloat = 10 + static let medium: CGFloat = 16 + static let large: CGFloat = 24 + } + + enum Radius { + static let card: CGFloat = 14 + static let image: CGFloat = 10 + static let pill: CGFloat = 999 + } + + enum Shadow { + static let card = (color: Color.black.opacity(0.14), radius: CGFloat(8), y: CGFloat(4)) + } } diff --git a/ios/Bookibra/Models/BookRemote.swift b/ios/Bookibra/Models/BookRemote.swift index 432a991..7a2334f 100644 --- a/ios/Bookibra/Models/BookRemote.swift +++ b/ios/Bookibra/Models/BookRemote.swift @@ -16,6 +16,8 @@ struct BookRemote: Codable, Identifiable, Hashable { let categories: [String] let publisher: String? let sourceLocale: String? + let readingStatus: ReadingStatus? + let readingProgress: Double? init( remoteId: String? = nil, @@ -30,7 +32,9 @@ struct BookRemote: Codable, Identifiable, Hashable { pageCount: Int? = nil, categories: [String] = [], publisher: String? = nil, - sourceLocale: String? = nil + sourceLocale: String? = nil, + readingStatus: ReadingStatus? = nil, + readingProgress: Double? = nil ) { self.remoteId = remoteId self.title = title @@ -45,6 +49,8 @@ struct BookRemote: Codable, Identifiable, Hashable { self.categories = categories self.publisher = publisher self.sourceLocale = sourceLocale + self.readingStatus = readingStatus + self.readingProgress = readingProgress } } diff --git a/ios/Bookibra/Models/LibraryBook.swift b/ios/Bookibra/Models/LibraryBook.swift index 4fb325a..11d2084 100644 --- a/ios/Bookibra/Models/LibraryBook.swift +++ b/ios/Bookibra/Models/LibraryBook.swift @@ -16,6 +16,9 @@ final class LibraryBook { var language: String? var sourceLocale: String? var remotePayloadJson: String? + // Optional tutulur ki eski store'lar migration sırasında kırılmasın. + var statusRaw: String? + var readingProgress: Double? init( localId: UUID = UUID(), @@ -30,7 +33,9 @@ final class LibraryBook { dateAdded: Date = .now, language: String? = nil, sourceLocale: String? = nil, - remotePayloadJson: String? = nil + remotePayloadJson: String? = nil, + statusRaw: String = ReadingStatus.wantToRead.rawValue, + readingProgress: Double = 0 ) { self.localId = localId self.title = title @@ -45,6 +50,8 @@ final class LibraryBook { self.language = language self.sourceLocale = sourceLocale self.remotePayloadJson = remotePayloadJson + self.statusRaw = statusRaw + self.readingProgress = min(max(readingProgress, 0), 1) } var authors: [String] { @@ -60,4 +67,14 @@ final class LibraryBook { .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } } + + var status: ReadingStatus { + get { ReadingStatus(rawValue: statusRaw ?? "") ?? .wantToRead } + set { statusRaw = newValue.rawValue } + } + + var readingProgressValue: Double { + get { min(max(readingProgress ?? 0, 0), 1) } + set { readingProgress = min(max(newValue, 0), 1) } + } } diff --git a/ios/Bookibra/Models/ReadingStatus.swift b/ios/Bookibra/Models/ReadingStatus.swift new file mode 100644 index 0000000..da450ec --- /dev/null +++ b/ios/Bookibra/Models/ReadingStatus.swift @@ -0,0 +1,34 @@ +import Foundation +import SwiftUI + +enum ReadingStatus: String, CaseIterable, Codable, Identifiable { + case wantToRead + case reading + case finished + + var id: String { rawValue } + + var title: String { + switch self { + case .wantToRead: return "Want to Read" + case .reading: return "Reading" + case .finished: return "Finished" + } + } + + var symbol: String { + switch self { + case .wantToRead: return "bookmark" + case .reading: return "book" + case .finished: return "checkmark.circle" + } + } + + var color: Color { + switch self { + case .wantToRead: return .orange + case .reading: return .blue + case .finished: return .green + } + } +} diff --git a/ios/Bookibra/ViewModels/BookDetailViewModel.swift b/ios/Bookibra/ViewModels/BookDetailViewModel.swift index cac93a0..27c71a3 100644 --- a/ios/Bookibra/ViewModels/BookDetailViewModel.swift +++ b/ios/Bookibra/ViewModels/BookDetailViewModel.swift @@ -37,7 +37,9 @@ final class BookDetailViewModel: ObservableObject { summary: book.description, language: book.language, sourceLocale: book.sourceLocale, - remotePayloadJson: nil + remotePayloadJson: nil, + statusRaw: (book.readingStatus ?? .wantToRead).rawValue, + readingProgress: book.readingProgress ?? 0 ) context.insert(local) isInLibrary = true diff --git a/ios/Bookibra/ViewModels/HomeViewModel.swift b/ios/Bookibra/ViewModels/HomeViewModel.swift index 9195041..b5b1d15 100644 --- a/ios/Bookibra/ViewModels/HomeViewModel.swift +++ b/ios/Bookibra/ViewModels/HomeViewModel.swift @@ -55,7 +55,9 @@ final class HomeViewModel: ObservableObject { language: local.language, description: local.summary, categories: local.categories, - sourceLocale: local.sourceLocale + sourceLocale: local.sourceLocale, + readingStatus: local.status, + readingProgress: local.readingProgressValue ) } } diff --git a/ios/Bookibra/Views/AddBooks/AddBooksView.swift b/ios/Bookibra/Views/AddBooks/AddBooksView.swift index bdcfda6..a405ddf 100644 --- a/ios/Bookibra/Views/AddBooks/AddBooksView.swift +++ b/ios/Bookibra/Views/AddBooks/AddBooksView.swift @@ -1,75 +1,46 @@ import SwiftUI +import SwiftData struct AddBooksView: View { + enum FlowStep: Int { + case source + case confirm + case categorize + case success + } + @EnvironmentObject private var router: AppRouter + @Environment(\.modelContext) private var modelContext @ObservedObject var viewModel: AddBooksViewModel + @State private var step: FlowStep = .source + @State private var selectedBook: BookRemote? + @State private var selectedCategory = "Design" + @State private var selectedStatus: ReadingStatus = .wantToRead + @State private var manualISBN = "" + @State private var startReadingNow = false + @State private var animateSuccess = false + + private let defaultCategories = ["Design", "Psychology", "Novels"] + var body: some View { - VStack(spacing: 12) { - Picker("Mode", selection: $viewModel.mode) { - ForEach(AddBooksViewModel.Mode.allCases, id: \.self) { mode in - Text(mode.title).tag(mode) - } - } - .pickerStyle(.segmented) + ScrollView { + VStack(spacing: Theme.Spacing.medium) { + progressHeader - Group { - switch viewModel.mode { - case .title: - titleSearch - case .scan: - scanSearch - case .filter: - filterSearch - } - } - - if let error = viewModel.errorMessage { - NetworkErrorView(message: error) { - Task { await viewModel.searchByTitle() } - } - } - - List(viewModel.results, id: \.id) { book in - Button { - router.path.append(.detail(book)) - } label: { - HStack(spacing: 12) { - AsyncImage(url: book.coverImageUrl) { phase in - if let image = phase.image { - image.resizable().scaledToFill() - } else { - RoundedRectangle(cornerRadius: 8).fill(.gray.opacity(0.2)) - } - } - .frame(width: 48, height: 72) - .clipShape(RoundedRectangle(cornerRadius: 8)) - - VStack(alignment: .leading, spacing: 4) { - Text(book.title) - .font(.headline) - .lineLimit(2) - Text(book.authors.joined(separator: ", ")) - .font(.subheadline) - .foregroundStyle(.secondary) - .lineLimit(1) - if let year = book.publishedYear { - Text("\(year)") - .font(.caption) - .foregroundStyle(.secondary) - } - } - } - } - } - .listStyle(.plain) - .overlay { - if viewModel.results.isEmpty, !viewModel.isLoading { - Text(String(localized: "common.empty")) - .font(.subheadline) - .foregroundStyle(.secondary) + switch step { + case .source: + sourceStep + case .confirm: + confirmStep + case .categorize: + categorizeStep + case .success: + successStep } } + .frame(maxWidth: .infinity, alignment: .top) + .padding(.bottom, 24) } .padding(.horizontal, 16) .navigationTitle(String(localized: "add.title")) @@ -80,36 +51,248 @@ struct AddBooksView: View { .controlSize(.large) } } + .sensoryFeedback(.success, trigger: animateSuccess) } - private var titleSearch: some View { - TextField(String(localized: "add.searchPlaceholder"), text: $viewModel.titleQuery) - .textFieldStyle(.roundedBorder) - .onChange(of: viewModel.titleQuery) { _, _ in - viewModel.titleChanged() + private var progressHeader: some View { + HStack(spacing: 8) { + ForEach(0..<4, id: \.self) { index in + Capsule() + .fill(index <= step.rawValue ? Color.accentColor : Color.gray.opacity(0.25)) + .frame(height: 6) + .animation(.easeOut(duration: 0.2), value: step.rawValue) } - } - - private var scanSearch: some View { - BarcodeScannerView { isbn in - Task { await viewModel.searchByISBN(isbn) } } - .frame(height: 260) - .clipShape(RoundedRectangle(cornerRadius: 16)) + .padding(.top, 4) } - private var filterSearch: some View { - VStack(spacing: 8) { - TextField("Title", text: $viewModel.filterTitle) - .textFieldStyle(.roundedBorder) - TextField("YYYY", text: $viewModel.filterYear) - .textFieldStyle(.roundedBorder) - .keyboardType(.numberPad) - Button("Apply") { - Task { await viewModel.applyFilter() } + private var sourceStep: some View { + VStack(spacing: Theme.Spacing.medium) { + Picker("Mode", selection: $viewModel.mode) { + ForEach(AddBooksViewModel.Mode.allCases, id: \.self) { mode in + Text(mode.title).tag(mode) + } + } + .pickerStyle(.segmented) + + Group { + switch viewModel.mode { + case .title: + TextField(String(localized: "add.searchPlaceholder"), text: $viewModel.titleQuery) + .textFieldStyle(.roundedBorder) + .onChange(of: viewModel.titleQuery) { _, _ in + viewModel.titleChanged() + } + case .scan: + VStack(spacing: 10) { + BarcodeScannerView { isbn in + Task { await viewModel.searchByISBN(isbn) } + } + .frame(height: 240) + .clipShape(RoundedRectangle(cornerRadius: 16)) + + HStack { + TextField("ISBN manuel gir", text: $manualISBN) + .textFieldStyle(.roundedBorder) + .keyboardType(.numberPad) + + Button("Ara") { + Task { await viewModel.searchByISBN(manualISBN) } + } + .buttonStyle(.borderedProminent) + } + } + case .filter: + VStack(spacing: 8) { + TextField("Title", text: $viewModel.filterTitle) + .textFieldStyle(.roundedBorder) + TextField("YYYY", text: $viewModel.filterYear) + .textFieldStyle(.roundedBorder) + .keyboardType(.numberPad) + Button("Apply") { + Task { await viewModel.applyFilter() } + } + .buttonStyle(.borderedProminent) + .frame(maxWidth: .infinity, alignment: .trailing) + } + } + } + + if let error = viewModel.errorMessage { + NetworkErrorView(message: error) { + Task { await viewModel.searchByTitle() } + } + } + + if viewModel.results.isEmpty, !viewModel.isLoading { + EmptyStateView( + symbol: "magnifyingglass", + title: "Sonuç bulunamadı", + message: "Farklı bir başlık dene veya ISBN'i manuel gir.", + buttonTitle: nil, + action: nil + ) + } else { + LazyVStack(spacing: 10) { + ForEach(viewModel.results, id: \.id) { book in + Button { + selectedBook = book + step = .confirm + } label: { + HStack(spacing: 12) { + AsyncImage(url: book.coverImageUrl) { image in + image.resizable().scaledToFill() + } placeholder: { + RoundedRectangle(cornerRadius: 8).fill(.gray.opacity(0.2)) + } + .frame(width: 48, height: 72) + .clipShape(RoundedRectangle(cornerRadius: 8)) + + VStack(alignment: .leading, spacing: 4) { + Text(book.title) + .font(.headline) + .lineLimit(2) + Text(book.authors.joined(separator: ", ")) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + } + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(.tertiary) + } + } + .buttonStyle(.plain) + .padding(12) + .background(Color.white.opacity(0.75), in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + } + } + } + } + } + + private var confirmStep: some View { + VStack(alignment: .leading, spacing: Theme.Spacing.medium) { + if let book = selectedBook { + BookCardView( + title: book.title, + author: book.authors.first ?? "", + coverURL: book.coverImageUrl, + status: .wantToRead, + progress: nil + ) + } + + HStack { + Button("Geri") { step = .source } + .buttonStyle(.bordered) + Spacer() + Button("Bilgileri Onayla") { step = .categorize } + .buttonStyle(.borderedProminent) + } + } + } + + private var categorizeStep: some View { + VStack(alignment: .leading, spacing: Theme.Spacing.medium) { + Text("Kategori & Durum") + .font(.title3.weight(.semibold)) + + Picker("Kategori", selection: $selectedCategory) { + ForEach(defaultCategories, id: \.self) { category in + Text(category).tag(category) + } + } + .pickerStyle(.menu) + + Picker("Durum", selection: $selectedStatus) { + ForEach(ReadingStatus.allCases) { status in + Text(status.title).tag(status) + } + } + .pickerStyle(.segmented) + + Toggle("Hemen okumaya başla", isOn: $startReadingNow) + + Button("Kitabı Kaydet") { + saveSelectedBook() } .buttonStyle(.borderedProminent) .frame(maxWidth: .infinity, alignment: .trailing) + + Button("Geri") { step = .confirm } + .buttonStyle(.bordered) } } + + private var successStep: some View { + VStack(spacing: Theme.Spacing.large) { + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 72)) + .foregroundStyle(.green) + .scaleEffect(animateSuccess ? 1 : 0.7) + .opacity(animateSuccess ? 1 : 0.4) + .animation(.spring(response: 0.5, dampingFraction: 0.7), value: animateSuccess) + + Text("Kitap başarıyla eklendi") + .font(.title3.weight(.semibold)) + + HStack { + Button("Yeni Kitap Ekle") { + resetFlow() + } + .buttonStyle(.bordered) + + Button("Kitaplığa Dön") { + router.selectedTab = .library + resetFlow() + } + .buttonStyle(.borderedProminent) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onAppear { animateSuccess = true } + } + + private func saveSelectedBook() { + guard let book = selectedBook else { return } + + let progress = startReadingNow ? 0.1 : (selectedStatus == .finished ? 1 : 0) + let status = startReadingNow ? ReadingStatus.reading : selectedStatus + + let local = LibraryBook( + title: book.title, + authorsString: book.authors.joined(separator: ", "), + coverUrlString: book.coverImageUrl?.absoluteString, + isbn10: book.isbn10, + isbn13: book.isbn13, + publishedYear: book.publishedYear, + categoriesString: selectedCategory, + summary: book.description, + language: book.language, + sourceLocale: book.sourceLocale, + remotePayloadJson: nil, + statusRaw: status.rawValue, + readingProgress: progress + ) + + modelContext.insert(local) + try? modelContext.save() + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + + withAnimation(.easeInOut(duration: 0.25)) { + step = .success + animateSuccess = false + } + } + + private func resetFlow() { + selectedBook = nil + selectedCategory = defaultCategories[0] + selectedStatus = .wantToRead + startReadingNow = false + manualISBN = "" + step = .source + animateSuccess = false + } } diff --git a/ios/Bookibra/Views/AddBooks/BarcodeScannerView.swift b/ios/Bookibra/Views/AddBooks/BarcodeScannerView.swift index c6b1a5c..1b61917 100644 --- a/ios/Bookibra/Views/AddBooks/BarcodeScannerView.swift +++ b/ios/Bookibra/Views/AddBooks/BarcodeScannerView.swift @@ -4,20 +4,62 @@ import VisionKit struct BarcodeScannerView: View { let onScanned: (String) -> Void + @State private var cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized var body: some View { Group { - if DataScannerViewController.isSupported, DataScannerViewController.isAvailable { - DataScannerRepresentable(onScanned: onScanned) + if cameraAuthorized { + if DataScannerViewController.isSupported, DataScannerViewController.isAvailable { + DataScannerRepresentable(onScanned: onScanned) + } else { + AVScannerRepresentable(onScanned: onScanned) + } } else { - AVScannerRepresentable(onScanned: onScanned) + scannerUnavailableView } } + .background(Color.black.opacity(0.85)) .overlay { RoundedRectangle(cornerRadius: 16) .stroke(Color.white.opacity(0.75), lineWidth: 2) } .accessibilityLabel("ISBN barkod tarayıcı") + .onAppear { + requestCameraPermissionIfNeeded() + } + } + + private var scannerUnavailableView: some View { + VStack(spacing: 10) { + Image(systemName: "camera.viewfinder") + .font(.system(size: 30)) + .foregroundStyle(.white.opacity(0.9)) + Text("Kamera erişimi gerekli") + .font(.headline) + .foregroundStyle(.white) + Text("Barkod taramak için Ayarlar > Gizlilik > Kamera üzerinden izin verin.") + .font(.footnote) + .foregroundStyle(.white.opacity(0.85)) + .multilineTextAlignment(.center) + .padding(.horizontal, 12) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + private func requestCameraPermissionIfNeeded() { + let status = AVCaptureDevice.authorizationStatus(for: .video) + switch status { + case .authorized: + cameraAuthorized = true + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { granted in + DispatchQueue.main.async { + cameraAuthorized = granted + } + } + default: + cameraAuthorized = false + } } } diff --git a/ios/Bookibra/Views/Category/CategoryListView.swift b/ios/Bookibra/Views/Category/CategoryListView.swift index 613a8c6..1160076 100644 --- a/ios/Bookibra/Views/Category/CategoryListView.swift +++ b/ios/Bookibra/Views/Category/CategoryListView.swift @@ -1,71 +1,111 @@ import SwiftUI import SwiftData +import UIKit struct CategoryListView: View { @EnvironmentObject private var router: AppRouter + @Environment(\.modelContext) private var modelContext @Query(sort: \LibraryBook.dateAdded, order: .reverse) private var allBooks: [LibraryBook] @ObservedObject var viewModel: CategoryViewModel var body: some View { let books = viewModel.books(from: allBooks) - VStack { - HStack { - TextField("Search", text: $viewModel.searchText) - .textFieldStyle(.roundedBorder) - Menu { - ForEach(CategoryViewModel.SortOption.allCases, id: \.self) { option in - Button(option.rawValue) { viewModel.sortOption = option } - } - } label: { - Image(systemName: "arrow.up.arrow.down.circle") - .font(.title3) - } - } - .padding(.horizontal) + List { + controlRow - ScrollView { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 104), spacing: 12)], spacing: 16) { - ForEach(books, id: \.localId) { book in - let remote = BookRemote( - title: book.title, - authors: book.authors, - publishedYear: book.publishedYear, - isbn10: book.isbn10, - isbn13: book.isbn13, - coverImageUrl: book.coverUrlString.flatMap(URL.init(string:)), - language: book.language, - description: book.summary, - categories: book.categories + if books.isEmpty { + EmptyStateView( + symbol: "books.vertical", + title: "Bu kategoride kitap yok", + message: "Kitap ekleyerek bu rafı doldurabilirsin.", + buttonTitle: "Kitap Keşfet", + action: { router.selectedTab = .discover } + ) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } else { + ForEach(books, id: \.localId) { book in + let remote = BookRemote( + title: book.title, + authors: book.authors, + publishedYear: book.publishedYear, + isbn10: book.isbn10, + isbn13: book.isbn13, + coverImageUrl: book.coverUrlString.flatMap(URL.init(string:)), + language: book.language, + description: book.summary, + categories: book.categories, + readingStatus: book.status, + readingProgress: book.readingProgressValue + ) + + Button { + router.path.append(.detail(remote)) + } label: { + BookCardView( + title: remote.title, + author: remote.authors.first ?? "", + coverURL: remote.coverImageUrl, + status: remote.readingStatus ?? .wantToRead, + progress: remote.readingProgress ) - - Button { - router.path.append(.detail(remote)) - } label: { - VStack(alignment: .leading, spacing: 6) { - AsyncImage(url: remote.coverImageUrl) { phase in - if let image = phase.image { - image.resizable().scaledToFill() - } else { - RoundedRectangle(cornerRadius: 10).fill(Color.gray.opacity(0.2)) - } - } - .frame(height: 154) - .clipShape(RoundedRectangle(cornerRadius: 10)) - - Text(book.title) - .font(.caption) - .lineLimit(2) - .foregroundStyle(.primary) - } - } + } + .buttonStyle(.plain) + .listRowInsets(EdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16)) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + statusButton(.finished, for: book) + statusButton(.reading, for: book) + statusButton(.wantToRead, for: book) } } - .padding(.horizontal) } } + .scrollContentBackground(.hidden) + .background(Theme.background.ignoresSafeArea()) .navigationTitle(viewModel.categoryName) .navigationBarTitleDisplayMode(.inline) - .background(Theme.background.ignoresSafeArea()) + } + + private var controlRow: some View { + HStack(spacing: 10) { + TextField("Kategori içinde ara", text: $viewModel.searchText) + .textFieldStyle(.roundedBorder) + + Menu { + ForEach(CategoryViewModel.SortOption.allCases, id: \.self) { option in + Button(option.rawValue) { viewModel.sortOption = option } + } + } label: { + Label("Sırala", systemImage: "arrow.up.arrow.down.circle") + } + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } + + private func statusButton(_ status: ReadingStatus, for book: LibraryBook) -> some View { + Button { + withAnimation(.spring(duration: 0.3)) { + book.status = status + if status == .finished { book.readingProgressValue = 1 } + if status == .wantToRead { book.readingProgressValue = 0 } + try? modelContext.save() + } + UIImpactFeedbackGenerator(style: .soft).impactOccurred() + } label: { + Label(status.title, systemImage: status.symbol) + } + .tint(color(for: status)) + } + + private func color(for status: ReadingStatus) -> Color { + switch status { + case .wantToRead: return .orange + case .reading: return .blue + case .finished: return .green + } } } diff --git a/ios/Bookibra/Views/Home/HomeView.swift b/ios/Bookibra/Views/Home/HomeView.swift index 8be5a1c..f2033d1 100644 --- a/ios/Bookibra/Views/Home/HomeView.swift +++ b/ios/Bookibra/Views/Home/HomeView.swift @@ -36,7 +36,9 @@ struct HomeView: View { BlurFogOverlay() .frame(height: 96) PrimaryPillButton(title: String(localized: "home.addBooks")) { - router.path.append(.addBooks) + withAnimation(.easeInOut(duration: 0.2)) { + router.selectedTab = .discover + } } .padding(.horizontal, 24) .padding(.bottom, 12)