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 1cbe989..b5832bd 100644 Binary files a/ios/Bookibra.xcodeproj/project.xcworkspace/xcuserdata/wisecolt.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/Bookibra.xcodeproj/project.xcworkspace/xcuserdata/wisecolt.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/Bookibra/App/AppRouter.swift b/ios/Bookibra/App/AppRouter.swift index d5cbb7a..15fdc93 100644 --- a/ios/Bookibra/App/AppRouter.swift +++ b/ios/Bookibra/App/AppRouter.swift @@ -3,17 +3,25 @@ import SwiftUI @MainActor final class AppRouter: ObservableObject { + enum Tab: Hashable { + case library + case discover + case stats + case profile + } + enum Route: Hashable { - case addBooks case category(name: String) case detail(BookRemote) } @Published var isAuthenticated = false + @Published var selectedTab: Tab = .library @Published var path: [Route] = [] func resetToHome() { path.removeAll() + selectedTab = .library } } diff --git a/ios/Bookibra/App/BookibraApp.swift b/ios/Bookibra/App/BookibraApp.swift index 7b21a34..084f013 100644 --- a/ios/Bookibra/App/BookibraApp.swift +++ b/ios/Bookibra/App/BookibraApp.swift @@ -33,15 +33,13 @@ private struct RootView: View { NavigationStack(path: $router.path) { Group { if router.isAuthenticated { - HomeView(viewModel: HomeViewModel()) + MainTabView() } else { AuthView(viewModel: AuthViewModel(authService: dependencies.authService, keychain: dependencies.keychain)) } } .navigationDestination(for: AppRouter.Route.self) { route in switch route { - case .addBooks: - AddBooksView(viewModel: AddBooksViewModel(booksService: dependencies.booksService)) case .category(let name): CategoryListView(viewModel: CategoryViewModel(categoryName: name)) case .detail(let book): @@ -70,3 +68,66 @@ private struct RootView: View { } } } + +private struct MainTabView: View { + @EnvironmentObject private var router: AppRouter + @Environment(\.dependencies) private var dependencies + + var body: some View { + TabView(selection: $router.selectedTab) { + HomeView(viewModel: HomeViewModel()) + .tabItem { + Label("Library", systemImage: "books.vertical") + } + .tag(AppRouter.Tab.library) + + AddBooksView(viewModel: AddBooksViewModel(booksService: dependencies.booksService)) + .tabItem { + Label("Discover", systemImage: "sparkle.magnifyingglass") + } + .tag(AppRouter.Tab.discover) + + StatsPlaceholderView() + .tabItem { + Label("Stats", systemImage: "chart.line.uptrend.xyaxis") + } + .tag(AppRouter.Tab.stats) + + ProfilePlaceholderView() + .tabItem { + Label("Profile", systemImage: "person.crop.circle") + } + .tag(AppRouter.Tab.profile) + } + } +} + +private struct StatsPlaceholderView: View { + var body: some View { + ContentUnavailableView { + Label("Stats", systemImage: "chart.bar.xaxis") + } description: { + Text("Reading analytics yakında eklenecek.") + } + } +} + +private struct ProfilePlaceholderView: View { + @EnvironmentObject private var router: AppRouter + @Environment(\.dependencies) private var dependencies + + var body: some View { + NavigationStack { + List { + Section("Session") { + Button("Logout", role: .destructive) { + _ = dependencies.keychain.delete(for: AuthViewModel.tokenKey) + router.isAuthenticated = false + router.resetToHome() + } + } + } + .navigationTitle("Profile") + } + } +} diff --git a/ios/Bookibra/DesignSystem/Components/BookCardView.swift b/ios/Bookibra/DesignSystem/Components/BookCardView.swift new file mode 100644 index 0000000..148b74a --- /dev/null +++ b/ios/Bookibra/DesignSystem/Components/BookCardView.swift @@ -0,0 +1,68 @@ +import SwiftUI + +struct BookCardView: View { + let title: String + let author: String + let coverURL: URL? + let status: ReadingStatus + let progress: Double? + + var body: some View { + VStack(alignment: .leading, spacing: Theme.Spacing.small) { + AsyncImage(url: coverURL) { phase in + switch phase { + case .success(let image): + image.resizable().scaledToFill() + default: + ZStack { + RoundedRectangle(cornerRadius: Theme.Radius.image) + .fill(Color.gray.opacity(0.22)) + Image(systemName: "book.closed") + .font(.title2) + .foregroundStyle(.secondary) + } + } + } + .frame(height: 170) + .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.image, style: .continuous)) + + Text(title) + .font(.headline) + .lineLimit(2) + .minimumScaleFactor(0.85) + + Text(author.isEmpty ? "Unknown Author" : author) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + + HStack(spacing: Theme.Spacing.xSmall) { + Image(systemName: status.symbol) + Text(status.title) + .lineLimit(1) + } + .font(.caption.weight(.semibold)) + .padding(.horizontal, 8) + .padding(.vertical, 5) + .foregroundStyle(status.color) + .background(status.color.opacity(0.14), in: Capsule()) + + if let progress, status == .reading { + ProgressView(value: progress) + .tint(status.color) + .animation(.easeOut(duration: 0.35), value: progress) + .accessibilityLabel("Reading progress") + .accessibilityValue("\(Int(progress * 100)) percent") + } + } + .padding(Theme.Spacing.medium) + .background(.background, in: RoundedRectangle(cornerRadius: Theme.Radius.card, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: Theme.Radius.card, style: .continuous) + .stroke(Color.primary.opacity(0.06), lineWidth: 1) + } + .shadow(color: Theme.Shadow.card.color, radius: Theme.Shadow.card.radius, y: Theme.Shadow.card.y) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(title), \(author), \(status.title)") + } +} diff --git a/ios/Bookibra/DesignSystem/Components/EmptyStateView.swift b/ios/Bookibra/DesignSystem/Components/EmptyStateView.swift new file mode 100644 index 0000000..ab09de4 --- /dev/null +++ b/ios/Bookibra/DesignSystem/Components/EmptyStateView.swift @@ -0,0 +1,34 @@ +import SwiftUI + +struct EmptyStateView: View { + let symbol: String + let title: String + let message: String + let buttonTitle: String? + let action: (() -> 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)