Compare commits

..

No commits in common. "aaf02071642a59ae375906bd9b9bcff1e8c33e92" and "5c0aeedc3d9b2f7a8427fb7973d2ce8ec24ae257" have entirely different histories.

42 changed files with 487 additions and 633 deletions

View File

@ -14,51 +14,64 @@
3D13F95F2B375DB800E91913 /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F95E2B375DB800E91913 /* ItemView.swift */; };
3D13F9612B37637500E91913 /* ItemMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F9602B37637500E91913 /* ItemMediaView.swift */; };
3D13F9652B37EC7A00E91913 /* ItemHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */; };
3D13F9692B389FA300E91913 /* ViewOffsetKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F9682B389FA300E91913 /* ViewOffsetKey.swift */; };
3D13F96F2B38A32500E91913 /* StickyHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F96E2B38A32500E91913 /* StickyHeaderView.swift */; };
3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */; };
3D21F4292B69A8B0007207D2 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 3D21F4282B69A8B0007207D2 /* ExpandableText */; };
3D2552462B7A847700192879 /* ItemSeriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2552452B7A847700192879 /* ItemSeriesView.swift */; };
3D2552492B7A8B3100192879 /* ItemSeriesSeasonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2552482B7A8B3100192879 /* ItemSeriesSeasonsView.swift */; };
3D2552492B7A8B3100192879 /* ItemSeriesSeriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */; };
3D3816C92B4B5648006414D7 /* ItemGenresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3816C82B4B5648006414D7 /* ItemGenresView.swift */; };
3D41D1FA2B2CAE0000E58234 /* ItemIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */; };
3D4C15722B3CAA670035373E /* DashboardSectionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4C15712B3CAA670035373E /* DashboardSectionTitleView.swift */; };
3D58F07E2B4DB19300DB2936 /* TextRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D58F07D2B4DB19300DB2936 /* TextRatingView.swift */; };
3D6A0DC12B867B4C001FDA40 /* ScreenSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6A0DC02B867B4C001FDA40 /* ScreenSize.swift */; };
3D6A7DB92B7C5D3900A5E31B /* ItemSeasonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */; };
3D74AE122B7D4EB000C17F2E /* UIScreenWidthExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D74AE112B7D4EB000C17F2E /* UIScreenWidthExtension.swift */; };
3D7709392B29139700199889 /* Pulse in Frameworks */ = {isa = PBXBuildFile; productRef = 3D7709382B29139700199889 /* Pulse */; };
3D77093B2B29139700199889 /* PulseUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3D77093A2B29139700199889 /* PulseUI */; };
3D7E479C2B89A4F4009B44C4 /* LibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D7E479B2B89A4F4009B44C4 /* LibrariesView.swift */; };
3D7E479E2B89A945009B44C4 /* LicenseItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D7E479D2B89A945009B44C4 /* LicenseItemView.swift */; };
3D8AB2A52B36440D005BD7D0 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */; };
3D8AB2A82B366353005BD7D0 /* LibraryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AB2A72B366353005BD7D0 /* LibraryDetailView.swift */; };
3D9063CB2B279A310063DD2A /* JelApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9063CA2B279A310063DD2A /* JelApp.swift */; };
3D9063CD2B279A310063DD2A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9063CC2B279A310063DD2A /* ContentView.swift */; };
3D9063CF2B279A320063DD2A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D9063CE2B279A320063DD2A /* Assets.xcassets */; };
3D9063D32B279A320063DD2A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D9063D22B279A320063DD2A /* Preview Assets.xcassets */; };
3D9063DD2B279A320063DD2A /* JelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9063DC2B279A320063DD2A /* JelTests.swift */; };
3D9063E72B279A320063DD2A /* JelUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9063E62B279A320063DD2A /* JelUITests.swift */; };
3D9063E92B279A320063DD2A /* JelUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9063E82B279A320063DD2A /* JelUITestsLaunchTests.swift */; };
3D9064592B27E4C70063DD2A /* JellyfinKit in Frameworks */ = {isa = PBXBuildFile; productRef = 3D9064582B27E4C70063DD2A /* JellyfinKit */; };
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDC82B28C62800919017 /* SignInView.swift */; };
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */; };
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */; };
3DADBBC42B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */; };
3DAFA8E82B38AFED00D71AD1 /* ItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */; };
3DAFA8EA2B39039900D71AD1 /* BaseItemDtoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */; };
3DAFA8EA2B39039900D71AD1 /* JellyfinKitExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E92B39039900D71AD1 /* JellyfinKitExtensions.swift */; };
3DAFA8EC2B394F9F00D71AD1 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */; };
3DAFA8EF2B3B707B00D71AD1 /* ItemMovieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8EE2B3B707B00D71AD1 /* ItemMovieView.swift */; };
3DBAC9E22B4C31BE005F8764 /* ItemPeopleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBAC9E12B4C31BE005F8764 /* ItemPeopleView.swift */; };
3DBAC9E42B4C7404005F8764 /* UIScreenCurrent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBAC9E32B4C7404005F8764 /* UIScreenCurrent.swift */; };
3DBAC9EA2B4C8927005F8764 /* ItemPersonIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBAC9E92B4C8927005F8764 /* ItemPersonIconView.swift */; };
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */; };
3DD6850C2B85A6A8002FAA1A /* BindingNot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD6850B2B85A6A8002FAA1A /* BindingNot.swift */; };
3DDD67932B293BC40026781E /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67922B293BC40026781E /* DashboardView.swift */; };
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67952B29E28B0026781E /* SettingsView.swift */; };
3DDF35D52B7D384000423923 /* ItemSeriesEpisodesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDF35D42B7D384000423923 /* ItemSeriesEpisodesView.swift */; };
3DDF35D72B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDF35D62B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift */; };
3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */; };
3DFE7AF72B5260FF005461FE /* ItemPersonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFE7AF62B5260FF005461FE /* ItemPersonView.swift */; };
3DFE7AF92B5264A4005461FE /* ItemPersonDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFE7AF82B5264A4005461FE /* ItemPersonDetailView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
3D9063D92B279A320063DD2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 3D9063BF2B279A310063DD2A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3D9063C62B279A310063DD2A;
remoteInfo = Jel;
};
3D9063E32B279A320063DD2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 3D9063BF2B279A310063DD2A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3D9063C62B279A310063DD2A;
remoteInfo = Jel;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
3D9064142B279A450063DD2A /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@ -79,19 +92,16 @@
3D13F95E2B375DB800E91913 /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = "<group>"; };
3D13F9602B37637500E91913 /* ItemMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemMediaView.swift; sourceTree = "<group>"; };
3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemHeaderView.swift; sourceTree = "<group>"; };
3D13F9682B389FA300E91913 /* ViewOffsetKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewOffsetKey.swift; sourceTree = "<group>"; };
3D13F96E2B38A32500E91913 /* StickyHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyHeaderView.swift; sourceTree = "<group>"; };
3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardLibraryView.swift; sourceTree = "<group>"; };
3D2552452B7A847700192879 /* ItemSeriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesView.swift; sourceTree = "<group>"; };
3D2552482B7A8B3100192879 /* ItemSeriesSeasonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesSeasonsView.swift; sourceTree = "<group>"; };
3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesSeriesView.swift; sourceTree = "<group>"; };
3D3816C82B4B5648006414D7 /* ItemGenresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemGenresView.swift; sourceTree = "<group>"; };
3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemIconView.swift; sourceTree = "<group>"; };
3D4C15712B3CAA670035373E /* DashboardSectionTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardSectionTitleView.swift; sourceTree = "<group>"; };
3D58F07D2B4DB19300DB2936 /* TextRatingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRatingView.swift; sourceTree = "<group>"; };
3D6A0DC02B867B4C001FDA40 /* ScreenSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSize.swift; sourceTree = "<group>"; };
3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeasonView.swift; sourceTree = "<group>"; };
3D74AE112B7D4EB000C17F2E /* UIScreenWidthExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreenWidthExtension.swift; sourceTree = "<group>"; };
3D7E479B2B89A4F4009B44C4 /* LibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrariesView.swift; sourceTree = "<group>"; };
3D7E479D2B89A945009B44C4 /* LicenseItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicenseItemView.swift; sourceTree = "<group>"; };
3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
3D8AB2A72B366353005BD7D0 /* LibraryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryDetailView.swift; sourceTree = "<group>"; };
3D9063C72B279A310063DD2A /* Jel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jel.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -100,12 +110,16 @@
3D9063CE2B279A320063DD2A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3D9063D02B279A320063DD2A /* Jel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Jel.entitlements; sourceTree = "<group>"; };
3D9063D22B279A320063DD2A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
3D9063D82B279A320063DD2A /* JelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3D9063DC2B279A320063DD2A /* JelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JelTests.swift; sourceTree = "<group>"; };
3D9063E22B279A320063DD2A /* JelUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JelUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3D9063E62B279A320063DD2A /* JelUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JelUITests.swift; sourceTree = "<group>"; };
3D9063E82B279A320063DD2A /* JelUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JelUITestsLaunchTests.swift; sourceTree = "<group>"; };
3D91FDC82B28C62800919017 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInToServerView.swift; sourceTree = "<group>"; };
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinDateFormatter.swift; sourceTree = "<group>"; };
3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesSelectableEpisodesView.swift; sourceTree = "<group>"; };
3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemInfoView.swift; sourceTree = "<group>"; };
3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseItemDtoExtensions.swift; sourceTree = "<group>"; };
3DAFA8E92B39039900D71AD1 /* JellyfinKitExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinKitExtensions.swift; sourceTree = "<group>"; };
3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; };
3DAFA8EE2B3B707B00D71AD1 /* ItemMovieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemMovieView.swift; sourceTree = "<group>"; };
3DBAC9E12B4C31BE005F8764 /* ItemPeopleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPeopleView.swift; sourceTree = "<group>"; };
@ -113,11 +127,8 @@
3DBAC9E92B4C8927005F8764 /* ItemPersonIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPersonIconView.swift; sourceTree = "<group>"; };
3DC0E5802B2832B9001CCE96 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3DC6BA2C2B2A422300416B9F /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
3DD6850B2B85A6A8002FAA1A /* BindingNot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingNot.swift; sourceTree = "<group>"; };
3DDD67922B293BC40026781E /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
3DDD67952B29E28B0026781E /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
3DDF35D42B7D384000423923 /* ItemSeriesEpisodesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesEpisodesView.swift; sourceTree = "<group>"; };
3DDF35D62B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesEpisodeIconView.swift; sourceTree = "<group>"; };
3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinClientController.swift; sourceTree = "<group>"; };
3DFE7AF62B5260FF005461FE /* ItemPersonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPersonView.swift; sourceTree = "<group>"; };
3DFE7AF82B5264A4005461FE /* ItemPersonDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPersonDetailView.swift; sourceTree = "<group>"; };
@ -137,14 +148,28 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063D52B279A320063DD2A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063DF2B279A320063DD2A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
3D1015D72B27F54A00F5C29A /* Views */ = {
isa = PBXGroup;
children = (
3D9063CC2B279A310063DD2A /* ContentView.swift */,
3D13F96D2B38A31300E91913 /* Utility */,
3D9063CC2B279A310063DD2A /* ContentView.swift */,
3DDD67902B293B780026781E /* Dashboard */,
3D13F95D2B375DAC00E91913 /* Item */,
3D8AB2A62B366309005BD7D0 /* Library */,
@ -160,11 +185,23 @@
3D1015E32B28000E00F5C29A /* AuthStateController.swift */,
3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */,
3DC6BA2C2B2A422300416B9F /* SettingsController.swift */,
3D6A0DC02B867B4C001FDA40 /* ScreenSize.swift */,
3DBAC9E32B4C7404005F8764 /* UIScreenCurrent.swift */,
);
path = Controllers;
sourceTree = "<group>";
};
3D1015E02B27FE5700F5C29A /* Models */ = {
isa = PBXGroup;
children = (
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */,
3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */,
3D13F9682B389FA300E91913 /* ViewOffsetKey.swift */,
3DAFA8E92B39039900D71AD1 /* JellyfinKitExtensions.swift */,
3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */,
);
path = Models;
sourceTree = "<group>";
};
3D13F95D2B375DAC00E91913 /* Item */ = {
isa = PBXGroup;
children = (
@ -193,10 +230,7 @@
3D2552472B7A8A9400192879 /* Series */ = {
isa = PBXGroup;
children = (
3D2552482B7A8B3100192879 /* ItemSeriesSeasonsView.swift */,
3DDF35D42B7D384000423923 /* ItemSeriesEpisodesView.swift */,
3DDF35D62B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift */,
3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */,
3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */,
);
path = Series;
sourceTree = "<group>";
@ -214,6 +248,8 @@
children = (
3DC0E5802B2832B9001CCE96 /* Info.plist */,
3D9063C92B279A310063DD2A /* Jel */,
3D9063DB2B279A320063DD2A /* JelTests */,
3D9063E52B279A320063DD2A /* JelUITests */,
3D1015D42B27F49000F5C29A /* JellyfinKit */,
3D9063C82B279A310063DD2A /* Products */,
);
@ -223,6 +259,8 @@
isa = PBXGroup;
children = (
3D9063C72B279A310063DD2A /* Jel.app */,
3D9063D82B279A320063DD2A /* JelTests.xctest */,
3D9063E22B279A320063DD2A /* JelUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -230,10 +268,10 @@
3D9063C92B279A310063DD2A /* Jel */ = {
isa = PBXGroup;
children = (
3D9063CA2B279A310063DD2A /* JelApp.swift */,
3DD6850A2B85A654002FAA1A /* Extensions */,
3D1015E02B27FE5700F5C29A /* Models */,
3D1015DF2B27F8EE00F5C29A /* Controllers */,
3D1015D72B27F54A00F5C29A /* Views */,
3D9063CA2B279A310063DD2A /* JelApp.swift */,
3D9063CE2B279A320063DD2A /* Assets.xcassets */,
3D9063D02B279A320063DD2A /* Jel.entitlements */,
3D9063D12B279A320063DD2A /* Preview Content */,
@ -249,6 +287,23 @@
path = "Preview Content";
sourceTree = "<group>";
};
3D9063DB2B279A320063DD2A /* JelTests */ = {
isa = PBXGroup;
children = (
3D9063DC2B279A320063DD2A /* JelTests.swift */,
);
path = JelTests;
sourceTree = "<group>";
};
3D9063E52B279A320063DD2A /* JelUITests */ = {
isa = PBXGroup;
children = (
3D9063E62B279A320063DD2A /* JelUITests.swift */,
3D9063E82B279A320063DD2A /* JelUITestsLaunchTests.swift */,
);
path = JelUITests;
sourceTree = "<group>";
};
3D91FDC52B28C28900919017 /* SignIn */ = {
isa = PBXGroup;
children = (
@ -263,9 +318,9 @@
isa = PBXGroup;
children = (
3DAFA8EE2B3B707B00D71AD1 /* ItemMovieView.swift */,
3DFE7AF62B5260FF005461FE /* ItemPersonView.swift */,
3D2552452B7A847700192879 /* ItemSeriesView.swift */,
3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */,
3DFE7AF62B5260FF005461FE /* ItemPersonView.swift */,
);
path = Types;
sourceTree = "<group>";
@ -280,20 +335,6 @@
path = Person;
sourceTree = "<group>";
};
3DD6850A2B85A654002FAA1A /* Extensions */ = {
isa = PBXGroup;
children = (
3DBAC9E32B4C7404005F8764 /* UIScreenCurrent.swift */,
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */,
3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */,
3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */,
3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */,
3D74AE112B7D4EB000C17F2E /* UIScreenWidthExtension.swift */,
3DD6850B2B85A6A8002FAA1A /* BindingNot.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
3DDD67902B293B780026781E /* Dashboard */ = {
isa = PBXGroup;
children = (
@ -308,8 +349,6 @@
isa = PBXGroup;
children = (
3DDD67952B29E28B0026781E /* SettingsView.swift */,
3D7E479B2B89A4F4009B44C4 /* LibrariesView.swift */,
3D7E479D2B89A945009B44C4 /* LicenseItemView.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -343,6 +382,42 @@
productReference = 3D9063C72B279A310063DD2A /* Jel.app */;
productType = "com.apple.product-type.application";
};
3D9063D72B279A320063DD2A /* JelTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3D9063EF2B279A320063DD2A /* Build configuration list for PBXNativeTarget "JelTests" */;
buildPhases = (
3D9063D42B279A320063DD2A /* Sources */,
3D9063D52B279A320063DD2A /* Frameworks */,
3D9063D62B279A320063DD2A /* Resources */,
);
buildRules = (
);
dependencies = (
3D9063DA2B279A320063DD2A /* PBXTargetDependency */,
);
name = JelTests;
productName = JelTests;
productReference = 3D9063D82B279A320063DD2A /* JelTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
3D9063E12B279A320063DD2A /* JelUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3D9063F22B279A320063DD2A /* Build configuration list for PBXNativeTarget "JelUITests" */;
buildPhases = (
3D9063DE2B279A320063DD2A /* Sources */,
3D9063DF2B279A320063DD2A /* Frameworks */,
3D9063E02B279A320063DD2A /* Resources */,
);
buildRules = (
);
dependencies = (
3D9063E42B279A320063DD2A /* PBXTargetDependency */,
);
name = JelUITests;
productName = JelUITests;
productReference = 3D9063E22B279A320063DD2A /* JelUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -356,6 +431,14 @@
3D9063C62B279A310063DD2A = {
CreatedOnToolsVersion = 15.0.1;
};
3D9063D72B279A320063DD2A = {
CreatedOnToolsVersion = 15.0.1;
TestTargetID = 3D9063C62B279A310063DD2A;
};
3D9063E12B279A320063DD2A = {
CreatedOnToolsVersion = 15.0.1;
TestTargetID = 3D9063C62B279A310063DD2A;
};
};
};
buildConfigurationList = 3D9063C22B279A310063DD2A /* Build configuration list for PBXProject "Jel" */;
@ -377,6 +460,8 @@
projectRoot = "";
targets = (
3D9063C62B279A310063DD2A /* Jel */,
3D9063D72B279A320063DD2A /* JelTests */,
3D9063E12B279A320063DD2A /* JelUITests */,
);
};
/* End PBXProject section */
@ -391,6 +476,20 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063D62B279A320063DD2A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063E02B279A320063DD2A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -398,43 +497,36 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3D6A0DC12B867B4C001FDA40 /* ScreenSize.swift in Sources */,
3D9063CD2B279A310063DD2A /* ContentView.swift in Sources */,
3DADBBC42B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift in Sources */,
3D13F96F2B38A32500E91913 /* StickyHeaderView.swift in Sources */,
3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */,
3D1015D92B27F57400F5C29A /* AddServerView.swift in Sources */,
3DDF35D52B7D384000423923 /* ItemSeriesEpisodesView.swift in Sources */,
3DAFA8EA2B39039900D71AD1 /* BaseItemDtoExtensions.swift in Sources */,
3DAFA8EA2B39039900D71AD1 /* JellyfinKitExtensions.swift in Sources */,
3D13F9652B37EC7A00E91913 /* ItemHeaderView.swift in Sources */,
3D3816C92B4B5648006414D7 /* ItemGenresView.swift in Sources */,
3DAFA8EC2B394F9F00D71AD1 /* ViewExtensions.swift in Sources */,
3D9063CB2B279A310063DD2A /* JelApp.swift in Sources */,
3DFE7AF92B5264A4005461FE /* ItemPersonDetailView.swift in Sources */,
3DBAC9E42B4C7404005F8764 /* UIScreenCurrent.swift in Sources */,
3D13F9692B389FA300E91913 /* ViewOffsetKey.swift in Sources */,
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */,
3D7E479C2B89A4F4009B44C4 /* LibrariesView.swift in Sources */,
3D58F07E2B4DB19300DB2936 /* TextRatingView.swift in Sources */,
3D6A7DB92B7C5D3900A5E31B /* ItemSeasonView.swift in Sources */,
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */,
3DAFA8EF2B3B707B00D71AD1 /* ItemMovieView.swift in Sources */,
3D8AB2A82B366353005BD7D0 /* LibraryDetailView.swift in Sources */,
3DDD67932B293BC40026781E /* DashboardView.swift in Sources */,
3DD6850C2B85A6A8002FAA1A /* BindingNot.swift in Sources */,
3D13F9612B37637500E91913 /* ItemMediaView.swift in Sources */,
3D2552492B7A8B3100192879 /* ItemSeriesSeasonsView.swift in Sources */,
3D2552492B7A8B3100192879 /* ItemSeriesSeriesView.swift in Sources */,
3DFE7AF72B5260FF005461FE /* ItemPersonView.swift in Sources */,
3D41D1FA2B2CAE0000E58234 /* ItemIconView.swift in Sources */,
3D8AB2A52B36440D005BD7D0 /* BlurHashDecode.swift in Sources */,
3DBAC9EA2B4C8927005F8764 /* ItemPersonIconView.swift in Sources */,
3DDF35D72B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift in Sources */,
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */,
3D74AE122B7D4EB000C17F2E /* UIScreenWidthExtension.swift in Sources */,
3DAFA8E82B38AFED00D71AD1 /* ItemInfoView.swift in Sources */,
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */,
3D4C15722B3CAA670035373E /* DashboardSectionTitleView.swift in Sources */,
3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */,
3D7E479E2B89A945009B44C4 /* LicenseItemView.swift in Sources */,
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */,
3D2552462B7A847700192879 /* ItemSeriesView.swift in Sources */,
3D13F95F2B375DB800E91913 /* ItemView.swift in Sources */,
@ -443,8 +535,38 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063D42B279A320063DD2A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3D9063DD2B279A320063DD2A /* JelTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3D9063DE2B279A320063DD2A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3D9063E92B279A320063DD2A /* JelUITestsLaunchTests.swift in Sources */,
3D9063E72B279A320063DD2A /* JelUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
3D9063DA2B279A320063DD2A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3D9063C62B279A310063DD2A /* Jel */;
targetProxy = 3D9063D92B279A320063DD2A /* PBXContainerItemProxy */;
};
3D9063E42B279A320063DD2A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3D9063C62B279A310063DD2A /* Jel */;
targetProxy = 3D9063E32B279A320063DD2A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
3D9063EA2B279A320063DD2A /* Debug */ = {
isa = XCBuildConfiguration;
@ -644,6 +766,100 @@
};
name = Release;
};
3D9063F02B279A320063DD2A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = H4TSJQG42B;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = icu.trinket.apps.JelTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Jel";
};
name = Debug;
};
3D9063F12B279A320063DD2A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = H4TSJQG42B;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = icu.trinket.apps.JelTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Jel";
};
name = Release;
};
3D9063F32B279A320063DD2A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = H4TSJQG42B;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = icu.trinket.apps.JelUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Jel;
};
name = Debug;
};
3D9063F42B279A320063DD2A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = H4TSJQG42B;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = icu.trinket.apps.JelUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Jel;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -665,6 +881,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3D9063EF2B279A320063DD2A /* Build configuration list for PBXNativeTarget "JelTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3D9063F02B279A320063DD2A /* Debug */,
3D9063F12B279A320063DD2A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3D9063F22B279A320063DD2A /* Build configuration list for PBXNativeTarget "JelUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3D9063F32B279A320063DD2A /* Debug */,
3D9063F42B279A320063DD2A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@ -680,8 +914,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/n3d1117/ExpandableText?tab=readme-ov-file";
requirement = {
kind = exactVersion;
version = 1.0.0;
branch = main;
kind = branch;
};
};
3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */ = {

View File

@ -5,9 +5,9 @@
"package": "ExpandableText",
"repositoryURL": "https://github.com/n3d1117/ExpandableText?tab=readme-ov-file",
"state": {
"branch": null,
"revision": "3707127edfb075d26f6f14c940702974ed887f33",
"version": "1.0.0"
"branch": "main",
"revision": "2b0dd2f3d2870fe933ffe5c2ee057d2e9fa647e2",
"version": null
}
},
{

View File

@ -14,8 +14,6 @@ class AuthStateController: ObservableObject {
@Published var userId: String?
@Published var username: String?
@Published var loaded: Bool = false
private let defaults = UserDefaults.standard
static let shared = AuthStateController()
@ -42,8 +40,6 @@ class AuthStateController: ObservableObject {
if let oldUsername = defaults.string(forKey: "AuthState_username") {
self.username = oldUsername
}
self.loaded = true
}
func save() {

View File

@ -1,12 +0,0 @@
//
// ScreenSize.swift
// Jel
//
// Created by zerocool on 2/21/24.
//
import Foundation
class ScreenSize: ObservableObject {
@Published var size: CGSize = CGSize(width: 0, height: 0)
}

View File

@ -1,18 +0,0 @@
//
// BindingNot.swift
// Jel
//
// Created by zerocool on 2/20/24.
//
import Foundation
import SwiftUI
extension Binding where Value == Bool {
var not: Binding<Value> {
Binding<Value>(
get: { !self.wrappedValue },
set: { self.wrappedValue = !$0 }
)
}
}

View File

@ -1,15 +0,0 @@
//
// UIScreenWidthExtension.swift
// Jel
//
// Created by zerocool on 2/14/24.
//
import Foundation
import UIKit
extension UIScreen{
static let screenWidth = UIScreen.main.bounds.size.width
static let screenHeight = UIScreen.main.bounds.size.height
static let screenSize = UIScreen.main.bounds.size
}

View File

@ -16,27 +16,16 @@ struct JelApp: App {
Version: Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.0",
Token: ""))
@StateObject var size: ScreenSize = ScreenSize()
var body: some Scene {
WindowGroup {
GeometryReader {geo in
ContentView()
.environmentObject(jellyfinClientController)
.environmentObject(size)
.onChange(of: geo.size) {
size.size = geo.size
}
.onAppear {
size.size = geo.size
}
.task {
AuthStateController.shared.load()
SettingsController.shared.load()
jellyfinClientController.setUrl(url: AuthStateController.shared.serverUrl)
jellyfinClientController.setToken(token: AuthStateController.shared.authToken ?? "")
}
}
ContentView()
.environmentObject(jellyfinClientController)
.task {
AuthStateController.shared.load()
SettingsController.shared.load()
jellyfinClientController.setUrl(url: AuthStateController.shared.serverUrl)
jellyfinClientController.setToken(token: AuthStateController.shared.authToken ?? "")
}
}
}
}

View File

@ -26,11 +26,3 @@ extension BaseItemDto {
return nil
}
}
extension BaseItemDto {
var overviewNL: String? {
get {
self.overview?.replacingOccurrences(of: "<br>", with: "\n")
}
}
}

View File

@ -0,0 +1,17 @@
//
// ViewOffsetKey.swift
// Jel
//
// Created by zerocool on 12/24/23.
//
import SwiftUI
/// A preference key to store ScrollView offset
public struct ViewOffsetKey: PreferenceKey {
public typealias Value = CGFloat
public static var defaultValue = CGFloat.zero
public static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}

View File

@ -9,46 +9,22 @@ import SwiftUI
import PulseUI
struct ContentView: View {
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var isSignedIn: Bool = true
@EnvironmentObject var jellyfinClient: JellyfinClientController
@StateObject var settingsController: SettingsController = SettingsController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var showingConsoleSheet: Bool = false
var body: some View {
VStack() {
if isSignedIn {
VStack {
if !authState.loggedIn {
SignInView()
} else {
NavigationStack {
DashboardView()
}
} else {
HStack {
Spacer()
VStack(alignment: .center) {
Spacer()
Text("You are not currently signed into a Jellyfin instance.")
.multilineTextAlignment(.center)
.padding()
Button {
// toggle logged in so that it invalidates isSignedIn
authState.loggedIn = true
authState.loggedIn = false
} label: {
Text("Sign in")
}
Spacer()
}
Spacer()
}
}
}
.sheet(isPresented: $isSignedIn.not) {
SignInView()
.interactiveDismissDisabled()
}
.onChange(of: authState.loggedIn, {
isSignedIn = authState.loggedIn
})
.onChange(of: authState.loaded, {
isSignedIn = authState.loggedIn
})
}
}

View File

@ -11,7 +11,7 @@ import JellyfinKit
struct DashboardLibraryView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var libraries: [BaseItemDto] = []
@State var loading: Bool = true
@ -37,8 +37,8 @@ struct DashboardLibraryView: View {
}
.buttonStyle(PlainButtonStyle())
}
}
}.padding(.horizontal)
}.padding(.horizontal)
}
}
.onAppear {
Task {

View File

@ -11,7 +11,6 @@ import JellyfinKit
struct DashboardView: View {
@State var showingSettingsSheet: Bool = false
var body: some View {
ScrollView {
LazyVStack {
@ -24,19 +23,19 @@ struct DashboardView: View {
.padding([.top, .leading])
}
}
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showingSettingsSheet.toggle()
} label: {
Label("Settings", systemImage: "gear")
}
}
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showingSettingsSheet.toggle()
} label: {
Label("Settings", systemImage: "gear")
}
}
.sheet(isPresented: $showingSettingsSheet) {
SettingsView(showingSettingsView: $showingSettingsSheet)
}
}
.sheet(isPresented: $showingSettingsSheet) {
SettingsView(showingSettingsView: $showingSettingsSheet)
}
}
}

View File

@ -11,7 +11,7 @@ import JellyfinKit
struct ItemGenresView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
var item: BaseItemDto
@State var libraryItems: [BaseItemDto]? = []

View File

@ -61,7 +61,6 @@ struct ItemIconView: View {
if shouldShowCaption {
Text(item.name ?? "Unknown")
.font(.subheadline)
.frame(width: width)
}
}
}
@ -71,7 +70,7 @@ struct ItemIconView: View {
case "Primary":
return item.imageBlurHashes?.primary?[item.imageTags?[imageType] ?? ""] ?? ""
case "Backdrop":
return item.imageBlurHashes?.backdrop?[item.backdropImageTags?.first ?? ""] ?? ""
return item.imageBlurHashes?.backdrop?[item.backdropImageTags?[0] ?? ""] ?? ""
default:
return ""
}

View File

@ -14,13 +14,9 @@ struct ItemInfoView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
if let productionYear = item.productionYear {
Text(String(productionYear))
}
if let genres = item.genres {
Text("")
Text(genres.first ?? "---")
}
Text((item.productionYear != nil) ? String(item.productionYear!) : "---")
Text("")
Text(item.genres?.first ?? "---")
}
HStack {

View File

@ -19,7 +19,7 @@ struct ItemMediaView: View {
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
ExpandableText((item.overviewNL ?? "").replacingOccurrences(of: "<br>", with: ""))
ExpandableText((item.overview ?? "").replacingOccurrences(of: "<br>", with: ""))
.lineLimit(8)
}
}

View File

@ -27,7 +27,7 @@ struct ItemView: View {
}
}
}
// .scrollIndicators(.hidden)
.scrollIndicators(.hidden)
}
}

View File

@ -9,7 +9,7 @@ import SwiftUI
import JellyfinKit
struct ItemPersonDetailView: View {
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@EnvironmentObject var jellyfinClient: JellyfinClientController
var person: BaseItemPerson

View File

@ -25,7 +25,7 @@ struct ItemPersonIconPlaceholderView: View {
}
struct ItemPersonIconView: View {
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@EnvironmentObject var jellyfinClient: JellyfinClientController
var person: BaseItemPerson

View File

@ -1,45 +0,0 @@
//
// ItemSeriesEpisodeIconView.swift
// Jel
//
// Created by zerocool on 2/14/24.
//
import SwiftUI
import JellyfinKit
import ExpandableText
struct ItemSeriesEpisodeIconView: View {
var item: BaseItemDto
@EnvironmentObject var size: ScreenSize
var iconWidthMultiplier: CGFloat = 0.5
var body: some View {
VStack(alignment: .leading) {
HStack {
ItemIconView(item: item,
width: (size.size.width * iconWidthMultiplier),
height: (size.size.width * iconWidthMultiplier) / 1.7,
contentMode: .fill)
VStack(alignment: .leading) {
Text("Episode \(item.indexNumber ?? 0)")
.foregroundStyle(.secondary)
.font(.callout)
Text(item.name ?? "---")
.bold()
}
.frame(height: (size.size.width * iconWidthMultiplier) / 1.7)
}
ExpandableText((item.overviewNL ?? "").replacingOccurrences(of: "<br>", with: "\n"))
.foregroundColor(.secondary)
}
}
}
//#Preview {
// ItemSeriesEpisodeIconView()
//}

View File

@ -1,56 +0,0 @@
//
// ItemSeriesEpisodesView.swift
// Jel
//
// Created by zerocool on 2/14/24.
//
import SwiftUI
import JellyfinKit
struct ItemSeriesEpisodesView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
var item: BaseItemDto
@State var episodeItems: [BaseItemDto] = []
@State var loading: Bool = true
var body: some View {
LazyVStack(alignment: .leading) {
ForEach(episodeItems) {episode in
ItemSeriesEpisodeIconView(item: episode)
}
}
.if(loading) {view in
view
.redacted(reason: .placeholder)
}
.onChange(of: item) {
self.loadEpisodes()
}
.onAppear {
self.loadEpisodes()
}
}
func loadEpisodes() {
Task {
let parameters = Paths.GetItemsParameters(
userID: authState.userId,
parentID: item.id ?? ""
)
let req = Paths.getItems(parameters: parameters)
do {
let res = try await jellyfinClient.send(req)
episodeItems = res.value.items ?? []
loading = false
} catch {}
}
}}
//#Preview {
// ItemSeriesEpisodesView()
//}

View File

@ -1,84 +0,0 @@
//
// ItemSeriesSelectableEpisodesView.swift
// Jel
//
// Created by zerocool on 2/24/24.
//
import SwiftUI
import JellyfinKit
struct Season: Identifiable, Hashable {
var name: String
var id: String
var season: BaseItemDto
}
extension Season {
static let emptySelection = Season(name: "", id: "", season: BaseItemDto())
}
struct ItemSeriesSelectableEpisodesView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@StateObject var authState: AuthStateController = AuthStateController.shared
var item: BaseItemDto // series
@State var seasons: [Season] = []
@State var currentSeason: Season = .emptySelection
@State var currentSeasonCopy: Season = .emptySelection
@State var loading: Bool = true
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Episodes")
.font(.title2)
.padding(.horizontal)
Picker("Season", selection: $currentSeason) {
ForEach(seasons, id: \.self) {season in
Text(season.name).tag(season)
}
}
}
ItemSeriesEpisodesView(item: currentSeasonCopy.season)
.padding(.horizontal)
}
.if(loading) {view in
view.redacted(reason: .placeholder)
}
.onChange(of: currentSeason) {
currentSeasonCopy = currentSeason
}
.onAppear {
Task {
let parameters = Paths.GetItemsParameters(
userID: authState.userId ?? "",
parentID: item.id ?? ""
)
let req = Paths.getItems(parameters: parameters)
do {
let res = try await jellyfinClient.send(req)
seasons = []
for season in res.value.items ?? [] {
let newSeason = Season(
name: season.name ?? "---",
id: season.id ?? "",
season: season
)
seasons.append(newSeason)
currentSeason = seasons.first ?? .emptySelection
loading = false
}
} catch {}
}
}
}
}
//#Preview {
// ItemSeriesSelectableEpisodesView()
//}

View File

@ -1,5 +1,5 @@
//
// ItemSeriesSeasonsView.swift
// ItemSeriesSeriesView.swift
// Jel
//
// Created by zerocool on 2/12/24.
@ -8,18 +8,17 @@
import SwiftUI
import JellyfinKit
struct ItemSeriesSeasonsView: View {
struct ItemSeriesSeriesView: View {
var item: BaseItemDto
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var seriesItems: [BaseItemDto] = []
@State var loading: Bool = true
var body: some View {
VStack(alignment: .leading) {
Text("Seasons")
Text("Series")
.font(.title2)
.padding(.horizontal)
@ -35,12 +34,9 @@ struct ItemSeriesSeasonsView: View {
}
}
}.padding(.horizontal)
}.scrollIndicators(.hidden)
}
}
.if(loading) {view in
view.redacted(reason: .placeholder)
}
.onAppear {
.onAppear{
Task {
let parameters = Paths.GetItemsParameters(
userID: authState.userId ?? "",
@ -52,8 +48,6 @@ struct ItemSeriesSeasonsView: View {
let res = try await jellyfinClient.send(req)
seriesItems = res.value.items ?? []
} catch {}
loading = false
}
}
}

View File

@ -14,7 +14,7 @@ struct ItemPersonViewItemsRow: View {
var body: some View {
ScrollView(.horizontal) {
HStack(alignment: .top) {
LazyHStack {
ForEach(items) {item in
NavigationLink {
ItemView(item: item)
@ -22,6 +22,7 @@ struct ItemPersonViewItemsRow: View {
ItemIconView(item: item, width: 150)
.showCaption()
.setAspectRatio(item.primaryImageAspectRatio ?? 0.6)
.padding()
}
.buttonStyle(PlainButtonStyle())
}
@ -44,7 +45,7 @@ struct ItemPersonView: View {
.font(.title)
.padding([.horizontal, .top])
if let overview = item.overviewNL {
if let overview = item.overview {
ExpandableText(overview)
.lineLimit(8)
.padding([.horizontal, .bottom])

View File

@ -13,16 +13,8 @@ struct ItemSeasonView: View {
var body: some View {
VStack {
if item.overviewNL != nil {
ItemMediaView(item: item)
.padding([.horizontal, .bottom])
}
ItemSeriesEpisodesView(item: item)
.padding(.horizontal)
ItemMediaView(item: item)
}
.navigationTitle(item.name ?? "Untitled")
.navigationBarTitleDisplayMode(.large)
}
}

View File

@ -34,9 +34,7 @@ struct ItemSeriesView: View {
ItemGenresView(item: item)
.foregroundStyle(Color.primary)
ItemSeriesSelectableEpisodesView(item: item)
ItemSeriesSeasonsView(item: item)
ItemSeriesSeriesView(item: item)
.foregroundStyle(Color.primary)
ItemPeopleView(item: item)

View File

@ -10,7 +10,7 @@ import JellyfinKit
struct LibraryDetailView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var library: BaseItemDto
@State var items: [BaseItemDto]? = []

View File

@ -1,162 +0,0 @@
//
// LibrariesView.swift
// Jel
//
// Created by zerocool on 2/23/24.
//
import SwiftUI
struct LibrariesView: View {
var body: some View {
VStack {
List {
LicenseItemView(
name: "ExpandableText",
url: "https://github.com/n3d1117/ExpandableText",
license: """
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
"""
)
LicenseItemView(
name: "CreateAPI",
url: "https://github.com/CreateAPI/CreateAPI",
license: """
MIT License
Copyright (c) 2022 Alexander Grebenyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
)
LicenseItemView(
name: "Get",
url: "https://github.com/kean/Get",
license: """
MIT License
Copyright (c) 2021-2022 Alexander Grebenyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
)
LicenseItemView(
name: "Nuke",
url: "https://github.com/kean/Nuke",
license: """
The MIT License (MIT)
Copyright (c) 2015-2024 Alexander Grebenyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
)
LicenseItemView(
name: "Pulse",
url: "https://github.com/kean/Pulse",
license: """
Copyright (c) 2020-2021 Alexander Grebenyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
)
}
}
.navigationTitle("Acknowledgements")
}
}
//#Preview {
// LibrariesView()
//}

View File

@ -1,29 +0,0 @@
//
// LicenseItemView.swift
// Jel
//
// Created by zerocool on 2/23/24.
//
import SwiftUI
import ExpandableText
struct LicenseItemView: View {
var name: String
var url: String
var license: String
var body: some View {
VStack(alignment: .leading) {
Link(name, destination: URL(string: url)!)
.bold()
ExpandableText(license)
.multilineTextAlignment(.leading)
}
}
}
//#Preview {
// LicenseItemView()
//}

View File

@ -11,9 +11,9 @@ import PulseUI
struct SettingsView: View {
@Binding var showingSettingsView: Bool
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@ObservedObject var settingsController: SettingsController = SettingsController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@ObservedObject var settingsController: SettingsController = SettingsController.shared
var body: some View {
NavigationStack {
Form {
@ -35,11 +35,7 @@ struct SettingsView: View {
Button(role: .destructive) {
authState.loggedIn = false
authState.authToken = nil
authState.save()
showingSettingsView.toggle()
settingsController.save()
} label: {
Text("Sign out")
}
@ -57,16 +53,6 @@ struct SettingsView: View {
} header: {
Text("Debug")
}
Section() {
NavigationLink {
LibrariesView()
} label: {
Text("Acknowledgements")
}
} header: {
Text("About")
}
}
.navigationTitle("Settings")
.toolbar {

View File

@ -12,7 +12,7 @@ struct AddServerView: View {
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@Binding var serverUrlIsValid: Bool
@State var serverUrlString: String = ""
@State var serverUrlString: String = "http://"
@State var urlHasError: Bool = false
@State var currentErrorMessage: String = ""
@State var isLoading: Bool = false
@ -25,7 +25,7 @@ struct AddServerView: View {
.font(.title)
HStack {
TextField(text: $serverUrlString) {
Text(verbatim: "https://jellyfin.example.com")
Text("http://jellyfin.example.com")
}
.keyboardType(.URL)
.textContentType(.URL)
@ -65,9 +65,6 @@ struct AddServerView: View {
.foregroundStyle(.red)
}
}
.onAppear {
serverUrlString = authState.serverUrl?.absoluteString ?? ""
}
}
func checkServerUrl() async {
@ -105,7 +102,7 @@ struct AddServerView: View {
}
//#Preview {
// AddServerView(serverUrlIsValid: .constant(false))
//
//}
#Preview {
AddServerView(serverUrlIsValid: .constant(false))
}

View File

@ -10,7 +10,7 @@ import SwiftUI
struct SignInToServerView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var username: String = ""
@State var password: String = ""

View File

@ -11,8 +11,9 @@ import PulseUI
struct SignInView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@ObservedObject var authState: AuthStateController = AuthStateController.shared
@StateObject var authState: AuthStateController = AuthStateController.shared
@State var serverUrlIsValid: Bool = false
@State var showingConsoleSheet: Bool = false
var body: some View {
NavigationStack {
@ -39,6 +40,6 @@ struct SignInView: View {
}
}
//#Preview {
// SignInView()
//}
#Preview {
SignInView()
}

View File

@ -8,6 +8,7 @@
import SwiftUI
struct StickyHeaderView<Content: View>: View {
var minHeight: CGFloat
var content: Content
@ -27,7 +28,7 @@ struct StickyHeaderView<Content: View>: View {
.frame(width: geo.size.width, height: geo.size.height + geo.frame(in: .global).minY)
}
}
.frame(minWidth: UIScreen.screenWidth, minHeight: minHeight)
.frame(minWidth: UIScreen.current?.bounds.width ?? 200, minHeight: minHeight)
}
}

35
JelTests/JelTests.swift Normal file
View File

@ -0,0 +1,35 @@
//
// JelTests.swift
// JelTests
//
// Created by zerocool on 12/11/23.
//
import XCTest
final class JelTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -0,0 +1,41 @@
//
// JelUITests.swift
// JelUITests
//
// Created by zerocool on 12/11/23.
//
import XCTest
final class JelUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}

View File

@ -0,0 +1,32 @@
//
// JelUITestsLaunchTests.swift
// JelUITests
//
// Created by zerocool on 12/11/23.
//
import XCTest
final class JelUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}

View File

@ -8,4 +8,3 @@ DONE
- Use Color.seondaryLabel for gray text
- Fix text wrapping on ItemPersonIconView
- License page