Start implementing dashboard view + async image loader
This commit is contained in:
parent
62a8e5704e
commit
5b78933de7
@ -11,8 +11,9 @@
|
|||||||
3D1015DC2B27F5D300F5C29A /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015DA2B27F5D300F5C29A /* Model.xcdatamodeld */; };
|
3D1015DC2B27F5D300F5C29A /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015DA2B27F5D300F5C29A /* Model.xcdatamodeld */; };
|
||||||
3D1015DE2B27F79900F5C29A /* DatamodelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015DD2B27F79900F5C29A /* DatamodelController.swift */; };
|
3D1015DE2B27F79900F5C29A /* DatamodelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015DD2B27F79900F5C29A /* DatamodelController.swift */; };
|
||||||
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015E32B28000E00F5C29A /* AuthStateController.swift */; };
|
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1015E32B28000E00F5C29A /* AuthStateController.swift */; };
|
||||||
|
3D16FC3A2B2CC22A00E6D8B3 /* BlurHashKit in Frameworks */ = {isa = PBXBuildFile; productRef = 3D16FC392B2CC22A00E6D8B3 /* BlurHashKit */; };
|
||||||
|
3D16FC3C2B2CDFB500E6D8B3 /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D16FC3B2B2CDFB500E6D8B3 /* LibraryView.swift */; };
|
||||||
3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F42B2C962500E58234 /* AppearancePicker.swift */; };
|
3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F42B2C962500E58234 /* AppearancePicker.swift */; };
|
||||||
3D41D1F72B2CAD2400E58234 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F62B2CAD2400E58234 /* BlurHashDecode.swift */; };
|
|
||||||
3D41D1FA2B2CAE0000E58234 /* LibraryIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */; };
|
3D41D1FA2B2CAE0000E58234 /* LibraryIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */; };
|
||||||
3D7709392B29139700199889 /* Pulse in Frameworks */ = {isa = PBXBuildFile; productRef = 3D7709382B29139700199889 /* Pulse */; };
|
3D7709392B29139700199889 /* Pulse in Frameworks */ = {isa = PBXBuildFile; productRef = 3D7709382B29139700199889 /* Pulse */; };
|
||||||
3D77093B2B29139700199889 /* PulseUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3D77093A2B29139700199889 /* PulseUI */; };
|
3D77093B2B29139700199889 /* PulseUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3D77093A2B29139700199889 /* PulseUI */; };
|
||||||
@ -27,8 +28,9 @@
|
|||||||
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDC82B28C62800919017 /* SignInView.swift */; };
|
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDC82B28C62800919017 /* SignInView.swift */; };
|
||||||
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */; };
|
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */; };
|
||||||
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */; };
|
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */; };
|
||||||
|
3DAA71C62B31E19200D5FB33 /* AsyncImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */; };
|
||||||
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */; };
|
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */; };
|
||||||
3DDD67932B293BC40026781E /* DashBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67922B293BC40026781E /* DashBoardView.swift */; };
|
3DDD67932B293BC40026781E /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67922B293BC40026781E /* DashboardView.swift */; };
|
||||||
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67952B29E28B0026781E /* SettingsView.swift */; };
|
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67952B29E28B0026781E /* SettingsView.swift */; };
|
||||||
3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */; };
|
3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@ -69,8 +71,8 @@
|
|||||||
3D1015DB2B27F5D300F5C29A /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
|
3D1015DB2B27F5D300F5C29A /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
|
||||||
3D1015DD2B27F79900F5C29A /* DatamodelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatamodelController.swift; sourceTree = "<group>"; };
|
3D1015DD2B27F79900F5C29A /* DatamodelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatamodelController.swift; sourceTree = "<group>"; };
|
||||||
3D1015E32B28000E00F5C29A /* AuthStateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStateController.swift; sourceTree = "<group>"; };
|
3D1015E32B28000E00F5C29A /* AuthStateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStateController.swift; sourceTree = "<group>"; };
|
||||||
|
3D16FC3B2B2CDFB500E6D8B3 /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = "<group>"; };
|
||||||
3D41D1F42B2C962500E58234 /* AppearancePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePicker.swift; sourceTree = "<group>"; };
|
3D41D1F42B2C962500E58234 /* AppearancePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePicker.swift; sourceTree = "<group>"; };
|
||||||
3D41D1F62B2CAD2400E58234 /* BlurHashDecode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlurHashDecode.swift; path = Jel/Models/BlurHashDecode.swift; sourceTree = SOURCE_ROOT; };
|
|
||||||
3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryIconView.swift; sourceTree = "<group>"; };
|
3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryIconView.swift; sourceTree = "<group>"; };
|
||||||
3D9063C72B279A310063DD2A /* Jel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
3D9063C72B279A310063DD2A /* Jel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3D9063CA2B279A310063DD2A /* JelApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JelApp.swift; sourceTree = "<group>"; };
|
3D9063CA2B279A310063DD2A /* JelApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JelApp.swift; sourceTree = "<group>"; };
|
||||||
@ -86,9 +88,10 @@
|
|||||||
3D91FDC82B28C62800919017 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.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>"; };
|
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>"; };
|
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinDateFormatter.swift; sourceTree = "<group>"; };
|
||||||
|
3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncImageView.swift; sourceTree = "<group>"; };
|
||||||
3DC0E5802B2832B9001CCE96 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; 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>"; };
|
3DC6BA2C2B2A422300416B9F /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
||||||
3DDD67922B293BC40026781E /* DashBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashBoardView.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>"; };
|
3DDD67952B29E28B0026781E /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinClientController.swift; sourceTree = "<group>"; };
|
3DF1ED3D2B282836000AD8EA /* JellyfinClientController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinClientController.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@ -98,6 +101,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3D16FC3A2B2CC22A00E6D8B3 /* BlurHashKit in Frameworks */,
|
||||||
3D77093B2B29139700199889 /* PulseUI in Frameworks */,
|
3D77093B2B29139700199889 /* PulseUI in Frameworks */,
|
||||||
3D7709392B29139700199889 /* Pulse in Frameworks */,
|
3D7709392B29139700199889 /* Pulse in Frameworks */,
|
||||||
3D9064592B27E4C70063DD2A /* JellyfinKit in Frameworks */,
|
3D9064592B27E4C70063DD2A /* JellyfinKit in Frameworks */,
|
||||||
@ -124,6 +128,7 @@
|
|||||||
3D1015D72B27F54A00F5C29A /* Views */ = {
|
3D1015D72B27F54A00F5C29A /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3DAA71C42B31E11D00D5FB33 /* Utility */,
|
||||||
3D9063CC2B279A310063DD2A /* ContentView.swift */,
|
3D9063CC2B279A310063DD2A /* ContentView.swift */,
|
||||||
3DDD67902B293B780026781E /* Dashboard */,
|
3DDD67902B293B780026781E /* Dashboard */,
|
||||||
3DDD67942B29E27A0026781E /* Settings */,
|
3DDD67942B29E27A0026781E /* Settings */,
|
||||||
@ -146,7 +151,6 @@
|
|||||||
3D1015E02B27FE5700F5C29A /* Models */ = {
|
3D1015E02B27FE5700F5C29A /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3D41D1F62B2CAD2400E58234 /* BlurHashDecode.swift */,
|
|
||||||
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */,
|
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
@ -155,6 +159,7 @@
|
|||||||
3D41D1F82B2CADF500E58234 /* Library */ = {
|
3D41D1F82B2CADF500E58234 /* Library */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3D16FC3B2B2CDFB500E6D8B3 /* LibraryView.swift */,
|
||||||
3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */,
|
3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */,
|
||||||
);
|
);
|
||||||
path = Library;
|
path = Library;
|
||||||
@ -232,11 +237,19 @@
|
|||||||
path = SignIn;
|
path = SignIn;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3DAA71C42B31E11D00D5FB33 /* Utility */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */,
|
||||||
|
);
|
||||||
|
path = Utility;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
3DDD67902B293B780026781E /* Dashboard */ = {
|
3DDD67902B293B780026781E /* Dashboard */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3D41D1F82B2CADF500E58234 /* Library */,
|
3D41D1F82B2CADF500E58234 /* Library */,
|
||||||
3DDD67922B293BC40026781E /* DashBoardView.swift */,
|
3DDD67922B293BC40026781E /* DashboardView.swift */,
|
||||||
);
|
);
|
||||||
path = Dashboard;
|
path = Dashboard;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -271,6 +284,7 @@
|
|||||||
3D9064582B27E4C70063DD2A /* JellyfinKit */,
|
3D9064582B27E4C70063DD2A /* JellyfinKit */,
|
||||||
3D7709382B29139700199889 /* Pulse */,
|
3D7709382B29139700199889 /* Pulse */,
|
||||||
3D77093A2B29139700199889 /* PulseUI */,
|
3D77093A2B29139700199889 /* PulseUI */,
|
||||||
|
3D16FC392B2CC22A00E6D8B3 /* BlurHashKit */,
|
||||||
);
|
);
|
||||||
productName = Jel;
|
productName = Jel;
|
||||||
productReference = 3D9063C72B279A310063DD2A /* Jel.app */;
|
productReference = 3D9063C72B279A310063DD2A /* Jel.app */;
|
||||||
@ -346,6 +360,7 @@
|
|||||||
mainGroup = 3D9063BE2B279A310063DD2A;
|
mainGroup = 3D9063BE2B279A310063DD2A;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */,
|
3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */,
|
||||||
|
3D16FC382B2CC22A00E6D8B3 /* XCRemoteSwiftPackageReference "BlurHashKit" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 3D9063C82B279A310063DD2A /* Products */;
|
productRefGroup = 3D9063C82B279A310063DD2A /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -397,11 +412,12 @@
|
|||||||
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */,
|
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */,
|
||||||
3D1015DC2B27F5D300F5C29A /* Model.xcdatamodeld in Sources */,
|
3D1015DC2B27F5D300F5C29A /* Model.xcdatamodeld in Sources */,
|
||||||
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */,
|
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */,
|
||||||
3DDD67932B293BC40026781E /* DashBoardView.swift in Sources */,
|
3DDD67932B293BC40026781E /* DashboardView.swift in Sources */,
|
||||||
3D41D1FA2B2CAE0000E58234 /* LibraryIconView.swift in Sources */,
|
3D41D1FA2B2CAE0000E58234 /* LibraryIconView.swift in Sources */,
|
||||||
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */,
|
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */,
|
||||||
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */,
|
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */,
|
||||||
3D41D1F72B2CAD2400E58234 /* BlurHashDecode.swift in Sources */,
|
3D16FC3C2B2CDFB500E6D8B3 /* LibraryView.swift in Sources */,
|
||||||
|
3DAA71C62B31E19200D5FB33 /* AsyncImageView.swift in Sources */,
|
||||||
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */,
|
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */,
|
||||||
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */,
|
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */,
|
||||||
3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */,
|
3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */,
|
||||||
@ -769,6 +785,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
3D16FC382B2CC22A00E6D8B3 /* XCRemoteSwiftPackageReference "BlurHashKit" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/LePips/BlurHashKit";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMinorVersion;
|
||||||
|
minimumVersion = 1.2.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */ = {
|
3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/kean/Pulse";
|
repositoryURL = "https://github.com/kean/Pulse";
|
||||||
@ -780,6 +804,11 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
3D16FC392B2CC22A00E6D8B3 /* BlurHashKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 3D16FC382B2CC22A00E6D8B3 /* XCRemoteSwiftPackageReference "BlurHashKit" */;
|
||||||
|
productName = BlurHashKit;
|
||||||
|
};
|
||||||
3D7709382B29139700199889 /* Pulse */ = {
|
3D7709382B29139700199889 /* Pulse */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */;
|
package = 3D7709372B29139700199889 /* XCRemoteSwiftPackageReference "Pulse" */;
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"object": {
|
"object": {
|
||||||
"pins": [
|
"pins": [
|
||||||
|
{
|
||||||
|
"package": "BlurHashKit",
|
||||||
|
"repositoryURL": "https://github.com/LePips/BlurHashKit",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "c0bd7423398de68cbeb3f99bff70f79c38bf36ab",
|
||||||
|
"version": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "Get",
|
"package": "Get",
|
||||||
"repositoryURL": "https://github.com/kean/Get",
|
"repositoryURL": "https://github.com/kean/Get",
|
||||||
|
@ -40,10 +40,13 @@ class JellyfinClientController: ObservableObject {
|
|||||||
|
|
||||||
self.api = APIClient(baseURL: serverUrl)
|
self.api = APIClient(baseURL: serverUrl)
|
||||||
self.setUrl(url: serverUrl)
|
self.setUrl(url: serverUrl)
|
||||||
|
|
||||||
|
self.setToken(token: self.authState.authToken ?? "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func setToken(token: String) {
|
func setToken(token: String) {
|
||||||
self.authHeaders.Token = token
|
self.authHeaders.Token = token
|
||||||
|
self.setUrl(url: self.authState.serverUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUrl(url: URL?) {
|
func setUrl(url: URL?) {
|
||||||
@ -62,6 +65,10 @@ class JellyfinClientController: ObservableObject {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUrl() -> URL? {
|
||||||
|
return self.api.configuration.baseURL
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult func send<T>(
|
@discardableResult func send<T>(
|
||||||
_ request: Request<T>,
|
_ request: Request<T>,
|
||||||
delegate: URLSessionDataDelegate? = nil,
|
delegate: URLSessionDataDelegate? = nil,
|
||||||
@ -101,7 +108,10 @@ extension JellyfinClientController {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.authState.loggedIn = true
|
self.authState.loggedIn = true
|
||||||
self.authState.authToken = res.value.accessToken
|
self.authState.authToken = res.value.accessToken
|
||||||
|
self.authState.userId = res.value.user?.id
|
||||||
self.authState.save()
|
self.authState.save()
|
||||||
|
|
||||||
|
self.setToken(token: self.authState.authToken ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ struct JelApp: App {
|
|||||||
.task {
|
.task {
|
||||||
AuthStateController.shared.load()
|
AuthStateController.shared.load()
|
||||||
SettingsController.shared.load()
|
SettingsController.shared.load()
|
||||||
|
jellyfinClientController.setUrl(url: AuthStateController.shared.serverUrl)
|
||||||
|
jellyfinClientController.setToken(token: AuthStateController.shared.authToken ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIImage {
|
|
||||||
public convenience init?(blurHash: String, size: CGSize, punch: Float = 1) {
|
|
||||||
guard blurHash.count >= 6 else { return nil }
|
|
||||||
|
|
||||||
let sizeFlag = String(blurHash[0]).decode83()
|
|
||||||
let numY = (sizeFlag / 9) + 1
|
|
||||||
let numX = (sizeFlag % 9) + 1
|
|
||||||
|
|
||||||
let quantisedMaximumValue = String(blurHash[1]).decode83()
|
|
||||||
let maximumValue = Float(quantisedMaximumValue + 1) / 166
|
|
||||||
|
|
||||||
guard blurHash.count == 4 + 2 * numX * numY else { return nil }
|
|
||||||
|
|
||||||
let colours: [(Float, Float, Float)] = (0 ..< numX * numY).map { i in
|
|
||||||
if i == 0 {
|
|
||||||
let value = String(blurHash[2 ..< 6]).decode83()
|
|
||||||
return decodeDC(value)
|
|
||||||
} else {
|
|
||||||
let value = String(blurHash[4 + i * 2 ..< 4 + i * 2 + 2]).decode83()
|
|
||||||
return decodeAC(value, maximumValue: maximumValue * punch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = Int(size.width)
|
|
||||||
let height = Int(size.height)
|
|
||||||
let bytesPerRow = width * 3
|
|
||||||
guard let data = CFDataCreateMutable(kCFAllocatorDefault, bytesPerRow * height) else { return nil }
|
|
||||||
CFDataSetLength(data, bytesPerRow * height)
|
|
||||||
guard let pixels = CFDataGetMutableBytePtr(data) else { return nil }
|
|
||||||
|
|
||||||
for y in 0 ..< height {
|
|
||||||
for x in 0 ..< width {
|
|
||||||
var r: Float = 0
|
|
||||||
var g: Float = 0
|
|
||||||
var b: Float = 0
|
|
||||||
|
|
||||||
for j in 0 ..< numY {
|
|
||||||
for i in 0 ..< numX {
|
|
||||||
let basis = cos(Float.pi * Float(x) * Float(i) / Float(width)) * cos(Float.pi * Float(y) * Float(j) / Float(height))
|
|
||||||
let colour = colours[i + j * numX]
|
|
||||||
r += colour.0 * basis
|
|
||||||
g += colour.1 * basis
|
|
||||||
b += colour.2 * basis
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let intR = UInt8(linearTosRGB(r))
|
|
||||||
let intG = UInt8(linearTosRGB(g))
|
|
||||||
let intB = UInt8(linearTosRGB(b))
|
|
||||||
|
|
||||||
pixels[3 * x + 0 + y * bytesPerRow] = intR
|
|
||||||
pixels[3 * x + 1 + y * bytesPerRow] = intG
|
|
||||||
pixels[3 * x + 2 + y * bytesPerRow] = intB
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
|
|
||||||
|
|
||||||
guard let provider = CGDataProvider(data: data) else { return nil }
|
|
||||||
guard let cgImage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 24, bytesPerRow: bytesPerRow,
|
|
||||||
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else { return nil }
|
|
||||||
|
|
||||||
self.init(cgImage: cgImage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func decodeDC(_ value: Int) -> (Float, Float, Float) {
|
|
||||||
let intR = value >> 16
|
|
||||||
let intG = (value >> 8) & 255
|
|
||||||
let intB = value & 255
|
|
||||||
return (sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB))
|
|
||||||
}
|
|
||||||
|
|
||||||
private func decodeAC(_ value: Int, maximumValue: Float) -> (Float, Float, Float) {
|
|
||||||
let quantR = value / (19 * 19)
|
|
||||||
let quantG = (value / 19) % 19
|
|
||||||
let quantB = value % 19
|
|
||||||
|
|
||||||
let rgb = (
|
|
||||||
signPow((Float(quantR) - 9) / 9, 2) * maximumValue,
|
|
||||||
signPow((Float(quantG) - 9) / 9, 2) * maximumValue,
|
|
||||||
signPow((Float(quantB) - 9) / 9, 2) * maximumValue
|
|
||||||
)
|
|
||||||
|
|
||||||
return rgb
|
|
||||||
}
|
|
||||||
|
|
||||||
private func signPow(_ value: Float, _ exp: Float) -> Float {
|
|
||||||
return copysign(pow(abs(value), exp), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func linearTosRGB(_ value: Float) -> Int {
|
|
||||||
let v = max(0, min(1, value))
|
|
||||||
if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) }
|
|
||||||
else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {
|
|
||||||
let v = Float(Int64(value)) / 255
|
|
||||||
if v <= 0.04045 { return v / 12.92 }
|
|
||||||
else { return pow((v + 0.055) / 1.055, 2.4) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private let encodeCharacters: [String] = {
|
|
||||||
return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".map { String($0) }
|
|
||||||
}()
|
|
||||||
|
|
||||||
private let decodeCharacters: [String: Int] = {
|
|
||||||
var dict: [String: Int] = [:]
|
|
||||||
for (index, character) in encodeCharacters.enumerated() {
|
|
||||||
dict[character] = index
|
|
||||||
}
|
|
||||||
return dict
|
|
||||||
}()
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
func decode83() -> Int {
|
|
||||||
var value: Int = 0
|
|
||||||
for character in self {
|
|
||||||
if let digit = decodeCharacters[String(character)] {
|
|
||||||
value = value * 83 + digit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension String {
|
|
||||||
subscript (offset: Int) -> Character {
|
|
||||||
return self[index(startIndex, offsetBy: offset)]
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript (bounds: CountableClosedRange<Int>) -> Substring {
|
|
||||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
|
||||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
|
||||||
return self[start...end]
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript (bounds: CountableRange<Int>) -> Substring {
|
|
||||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
|
||||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
|
||||||
return self[start..<end]
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ struct ContentView: View {
|
|||||||
SignInView()
|
SignInView()
|
||||||
} else {
|
} else {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
DashBoardView()
|
DashboardView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
//
|
//
|
||||||
// DashBoardView.swift
|
// DashboardView.swift
|
||||||
// Jel
|
// Jel
|
||||||
//
|
//
|
||||||
// Created by zerocool on 12/12/23.
|
// Created by zerocool on 12/12/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import JellyfinKit
|
||||||
|
|
||||||
struct DashBoardView: View {
|
struct DashboardView: View {
|
||||||
@State var showingSettingsSheet: Bool = false
|
@State var showingSettingsSheet: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
VStack {
|
VStack {
|
||||||
|
LibraryView()
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
@ -32,5 +33,5 @@ struct DashBoardView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
DashBoardView()
|
DashboardView()
|
||||||
}
|
}
|
@ -6,13 +6,29 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import JellyfinKit
|
||||||
|
import BlurHashKit
|
||||||
|
|
||||||
struct LibraryIconView: View {
|
struct LibraryIconView: View {
|
||||||
|
@EnvironmentObject var jellyfinClient: JellyfinClientController
|
||||||
|
|
||||||
|
@State var library: BaseItemDto
|
||||||
|
@State var loadingImage: Bool = true
|
||||||
|
|
||||||
|
@State var loadedImageBinaryData: Data?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
VStack {
|
||||||
|
AsyncImageView(imageId: library.id ?? "",
|
||||||
|
blurhash: library.imageBlurHashes?.primary?[library.imageTags?["Primary"] ?? ""] ?? "",
|
||||||
|
imageType: "Primary")
|
||||||
|
|
||||||
|
Text(library.name ?? "Unknown")
|
||||||
|
.font(.subheadline)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
LibraryIconView()
|
LibraryIconView(library: BaseItemDto())
|
||||||
}
|
}
|
||||||
|
44
Jel/Views/Dashboard/Library/LibraryView.swift
Normal file
44
Jel/Views/Dashboard/Library/LibraryView.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// LibraryView.swift
|
||||||
|
// Jel
|
||||||
|
//
|
||||||
|
// Created by zerocool on 12/15/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import JellyfinKit
|
||||||
|
|
||||||
|
struct LibraryView: View {
|
||||||
|
@EnvironmentObject var jellyfinClient: JellyfinClientController
|
||||||
|
|
||||||
|
@StateObject var authState: AuthStateController = AuthStateController.shared
|
||||||
|
|
||||||
|
@State var libraries: [BaseItemDto] = []
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack {
|
||||||
|
ForEach(libraries) {library in
|
||||||
|
if library.collectionType == "movies" || library.collectionType == "tvshows" {
|
||||||
|
LibraryIconView(library: library)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let request = Paths.getUserViews(userID: authState.userId ?? "")
|
||||||
|
if let results = try await jellyfinClient.send(request).value.items {
|
||||||
|
libraries = results
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#Preview {
|
||||||
|
// LibraryView()
|
||||||
|
//}
|
50
Jel/Views/Utility/AsyncImageView.swift
Normal file
50
Jel/Views/Utility/AsyncImageView.swift
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// AsyncImageView.swift
|
||||||
|
// Jel
|
||||||
|
//
|
||||||
|
// Created by zerocool on 12/19/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import BlurHashKit
|
||||||
|
import JellyfinKit
|
||||||
|
|
||||||
|
struct AsyncImageView: View {
|
||||||
|
@EnvironmentObject var jellyfinClient: JellyfinClientController
|
||||||
|
|
||||||
|
@State var imageId: String
|
||||||
|
@State var blurhash: String
|
||||||
|
@State var imageType: String
|
||||||
|
|
||||||
|
@State var loading = true
|
||||||
|
@State var uiImage: UIImage = UIImage()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
if loading {
|
||||||
|
BlurHashView(blurHash: blurhash)
|
||||||
|
} else {
|
||||||
|
Image(uiImage: uiImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
Task {
|
||||||
|
let request = Paths.getItemImage(itemID: imageId, imageType: imageType)
|
||||||
|
do {
|
||||||
|
let res = try await jellyfinClient.send(request)
|
||||||
|
if let image = UIImage(data: res.value) {
|
||||||
|
uiImage = image
|
||||||
|
loading = false
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#Preview {
|
||||||
|
// AsyncImageView(imageId: "", blurhash: "", imageType: "")
|
||||||
|
//}
|
Loading…
Reference in New Issue
Block a user