Basic SVG Support (#1010)
This commit is contained in:
parent
bda46325b3
commit
8c9c86713d
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// Swiftfin is subject to the terms of the Mozilla Public
|
||||||
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||||
|
//
|
||||||
|
|
||||||
|
import SVGKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// Note: SVGKit does not support the simulator and will appear blank.
|
||||||
|
|
||||||
|
// This seemed necessary because using SwiftUI `Image(uiImage:)` would cause severe lag.
|
||||||
|
struct FastSVGView: UIViewRepresentable {
|
||||||
|
|
||||||
|
let data: Data
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> some UIView {
|
||||||
|
let imageView = SVGKFastImageView(svgkImage: SVGKImage(data: data))!
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
imageView.clipsToBounds = true
|
||||||
|
|
||||||
|
imageView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||||
|
imageView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
|
imageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||||
|
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||||
|
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UIViewType, context: Context) {}
|
||||||
|
}
|
|
@ -7,16 +7,24 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import BlurHashKit
|
import BlurHashKit
|
||||||
import JellyfinAPI
|
|
||||||
import Nuke
|
import Nuke
|
||||||
import NukeUI
|
import NukeUI
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
|
||||||
|
|
||||||
private let imagePipeline = ImagePipeline(configuration: .withDataCache)
|
private let imagePipeline = {
|
||||||
|
|
||||||
|
ImageDecoderRegistry.shared.register { context in
|
||||||
|
guard let mimeType = context.urlResponse?.mimeType else { return nil }
|
||||||
|
return mimeType.contains("svg") ? ImageDecoders.Empty() : nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImagePipeline(configuration: .withDataCache)
|
||||||
|
}()
|
||||||
|
|
||||||
// TODO: Binding inits?
|
// TODO: Binding inits?
|
||||||
// - instead of removing first source on failure, just safe index into sources
|
// - instead of removing first source on failure, just safe index into sources
|
||||||
|
// TODO: currently SVGs are only supported for logos, which are only used in a few places.
|
||||||
|
// make it so when displaying an SVG there is a unified `image` caller modifier
|
||||||
struct ImageView: View {
|
struct ImageView: View {
|
||||||
|
|
||||||
@State
|
@State
|
||||||
|
@ -42,8 +50,12 @@ struct ImageView: View {
|
||||||
if state.isLoading {
|
if state.isLoading {
|
||||||
_placeholder(currentSource)
|
_placeholder(currentSource)
|
||||||
} else if let _image = state.image {
|
} else if let _image = state.image {
|
||||||
image(_image.resizable())
|
if let data = state.imageContainer?.data {
|
||||||
.eraseToAnyView()
|
FastSVGView(data: data)
|
||||||
|
} else {
|
||||||
|
image(_image.resizable())
|
||||||
|
.eraseToAnyView()
|
||||||
|
}
|
||||||
} else if state.error != nil {
|
} else if state.error != nil {
|
||||||
failure()
|
failure()
|
||||||
.eraseToAnyView()
|
.eraseToAnyView()
|
||||||
|
@ -103,7 +115,7 @@ extension ImageView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Extensions
|
// MARK: Modifiers
|
||||||
|
|
||||||
extension ImageView {
|
extension ImageView {
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,10 @@
|
||||||
E1153DAF2BBA734200424D36 /* CollectionHStack in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DAE2BBA734200424D36 /* CollectionHStack */; };
|
E1153DAF2BBA734200424D36 /* CollectionHStack in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DAE2BBA734200424D36 /* CollectionHStack */; };
|
||||||
E1153DB12BBA734C00424D36 /* CollectionHStack in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DB02BBA734C00424D36 /* CollectionHStack */; };
|
E1153DB12BBA734C00424D36 /* CollectionHStack in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DB02BBA734C00424D36 /* CollectionHStack */; };
|
||||||
E1153DB42BBA80FB00424D36 /* EmptyCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DB22BBA80B400424D36 /* EmptyCard.swift */; };
|
E1153DB42BBA80FB00424D36 /* EmptyCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DB22BBA80B400424D36 /* EmptyCard.swift */; };
|
||||||
|
E1153DCC2BBB633B00424D36 /* FastSVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DCB2BBB633B00424D36 /* FastSVGView.swift */; };
|
||||||
|
E1153DCD2BBB633B00424D36 /* FastSVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DCB2BBB633B00424D36 /* FastSVGView.swift */; };
|
||||||
|
E1153DD02BBB634F00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DCF2BBB634F00424D36 /* SVGKit */; };
|
||||||
|
E1153DD22BBB649C00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DD12BBB649C00424D36 /* SVGKit */; };
|
||||||
E1171A1928A2212600FA1AF5 /* QuickConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */; };
|
E1171A1928A2212600FA1AF5 /* QuickConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */; };
|
||||||
E118959D289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
E118959D289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
||||||
E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
||||||
|
@ -973,6 +977,7 @@
|
||||||
E1153D992BBA3E9800424D36 /* ErrorCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorCard.swift; sourceTree = "<group>"; };
|
E1153D992BBA3E9800424D36 /* ErrorCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorCard.swift; sourceTree = "<group>"; };
|
||||||
E1153D9B2BBA3E9D00424D36 /* LoadingCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingCard.swift; sourceTree = "<group>"; };
|
E1153D9B2BBA3E9D00424D36 /* LoadingCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingCard.swift; sourceTree = "<group>"; };
|
||||||
E1153DB22BBA80B400424D36 /* EmptyCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCard.swift; sourceTree = "<group>"; };
|
E1153DB22BBA80B400424D36 /* EmptyCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCard.swift; sourceTree = "<group>"; };
|
||||||
|
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FastSVGView.swift; sourceTree = "<group>"; };
|
||||||
E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectView.swift; sourceTree = "<group>"; };
|
E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectView.swift; sourceTree = "<group>"; };
|
||||||
E118959C289312020042947B /* BaseItemPerson+Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemPerson+Poster.swift"; sourceTree = "<group>"; };
|
E118959C289312020042947B /* BaseItemPerson+Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemPerson+Poster.swift"; sourceTree = "<group>"; };
|
||||||
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewOffsetModifier.swift; sourceTree = "<group>"; };
|
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewOffsetModifier.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1361,6 +1366,7 @@
|
||||||
E13DD3CD27164CA7009D4DAF /* CoreStore in Frameworks */,
|
E13DD3CD27164CA7009D4DAF /* CoreStore in Frameworks */,
|
||||||
E1A7B1652B9A9F7800152546 /* PreferencesView in Frameworks */,
|
E1A7B1652B9A9F7800152546 /* PreferencesView in Frameworks */,
|
||||||
E1153DA92BBA642A00424D36 /* CollectionVGrid in Frameworks */,
|
E1153DA92BBA642A00424D36 /* CollectionVGrid in Frameworks */,
|
||||||
|
E1153DD22BBB649C00424D36 /* SVGKit in Frameworks */,
|
||||||
62666E1527E501C800EC0ECD /* AVFoundation.framework in Frameworks */,
|
62666E1527E501C800EC0ECD /* AVFoundation.framework in Frameworks */,
|
||||||
E13AF3BC28A0C59E009093AB /* BlurHashKit in Frameworks */,
|
E13AF3BC28A0C59E009093AB /* BlurHashKit in Frameworks */,
|
||||||
E1153DB12BBA734C00424D36 /* CollectionHStack in Frameworks */,
|
E1153DB12BBA734C00424D36 /* CollectionHStack in Frameworks */,
|
||||||
|
@ -1402,6 +1408,7 @@
|
||||||
62666E0C27E501A500EC0ECD /* OpenGLES.framework in Frameworks */,
|
62666E0C27E501A500EC0ECD /* OpenGLES.framework in Frameworks */,
|
||||||
E19E6E0A28A0BEFF005C10C8 /* BlurHashKit in Frameworks */,
|
E19E6E0A28A0BEFF005C10C8 /* BlurHashKit in Frameworks */,
|
||||||
E1FAD1C62A0375BA007F5521 /* UDPBroadcast in Frameworks */,
|
E1FAD1C62A0375BA007F5521 /* UDPBroadcast in Frameworks */,
|
||||||
|
E1153DD02BBB634F00424D36 /* SVGKit in Frameworks */,
|
||||||
E18D6AA62BAA96F000A0D167 /* CollectionHStack in Frameworks */,
|
E18D6AA62BAA96F000A0D167 /* CollectionHStack in Frameworks */,
|
||||||
62666E0127E5016900EC0ECD /* CoreFoundation.framework in Frameworks */,
|
62666E0127E5016900EC0ECD /* CoreFoundation.framework in Frameworks */,
|
||||||
E14CB6862A9FF62A001586C6 /* JellyfinAPI in Frameworks */,
|
E14CB6862A9FF62A001586C6 /* JellyfinAPI in Frameworks */,
|
||||||
|
@ -2639,6 +2646,7 @@
|
||||||
children = (
|
children = (
|
||||||
E104DC952B9E7E29008F506D /* AssertionFailureView.swift */,
|
E104DC952B9E7E29008F506D /* AssertionFailureView.swift */,
|
||||||
E18E0203288749200022598C /* BlurView.swift */,
|
E18E0203288749200022598C /* BlurView.swift */,
|
||||||
|
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */,
|
||||||
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
||||||
E1D37F472B9C648E00343D2B /* MaxHeightText.swift */,
|
E1D37F472B9C648E00343D2B /* MaxHeightText.swift */,
|
||||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */,
|
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */,
|
||||||
|
@ -2990,6 +2998,7 @@
|
||||||
E1392FEC2BA218A80034110D /* SwiftUIIntrospect */,
|
E1392FEC2BA218A80034110D /* SwiftUIIntrospect */,
|
||||||
E1153DA82BBA642A00424D36 /* CollectionVGrid */,
|
E1153DA82BBA642A00424D36 /* CollectionVGrid */,
|
||||||
E1153DB02BBA734C00424D36 /* CollectionHStack */,
|
E1153DB02BBA734C00424D36 /* CollectionHStack */,
|
||||||
|
E1153DD12BBB649C00424D36 /* SVGKit */,
|
||||||
);
|
);
|
||||||
productName = "JellyfinPlayer tvOS";
|
productName = "JellyfinPlayer tvOS";
|
||||||
productReference = 535870602669D21600D05A09 /* Swiftfin tvOS.app */;
|
productReference = 535870602669D21600D05A09 /* Swiftfin tvOS.app */;
|
||||||
|
@ -3043,6 +3052,7 @@
|
||||||
E1153DA62BBA641000424D36 /* CollectionVGrid */,
|
E1153DA62BBA641000424D36 /* CollectionVGrid */,
|
||||||
E1153DAB2BBA6AD200424D36 /* CollectionHStack */,
|
E1153DAB2BBA6AD200424D36 /* CollectionHStack */,
|
||||||
E1153DAE2BBA734200424D36 /* CollectionHStack */,
|
E1153DAE2BBA734200424D36 /* CollectionHStack */,
|
||||||
|
E1153DCF2BBB634F00424D36 /* SVGKit */,
|
||||||
);
|
);
|
||||||
productName = JellyfinPlayer;
|
productName = JellyfinPlayer;
|
||||||
productReference = 5377CBF1263B596A003A4E83 /* Swiftfin iOS.app */;
|
productReference = 5377CBF1263B596A003A4E83 /* Swiftfin iOS.app */;
|
||||||
|
@ -3114,6 +3124,7 @@
|
||||||
E15D4F032B1B0C3C00442DB8 /* XCLocalSwiftPackageReference "PreferencesView" */,
|
E15D4F032B1B0C3C00442DB8 /* XCLocalSwiftPackageReference "PreferencesView" */,
|
||||||
E1153DA52BBA641000424D36 /* XCRemoteSwiftPackageReference "CollectionVGrid" */,
|
E1153DA52BBA641000424D36 /* XCRemoteSwiftPackageReference "CollectionVGrid" */,
|
||||||
E1153DAD2BBA734200424D36 /* XCRemoteSwiftPackageReference "CollectionHStack" */,
|
E1153DAD2BBA734200424D36 /* XCRemoteSwiftPackageReference "CollectionHStack" */,
|
||||||
|
E1153DCE2BBB634F00424D36 /* XCRemoteSwiftPackageReference "SVGKit" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
@ -3435,6 +3446,7 @@
|
||||||
E12E30F5296392EC0022FAC9 /* EnumPickerView.swift in Sources */,
|
E12E30F5296392EC0022FAC9 /* EnumPickerView.swift in Sources */,
|
||||||
E1575E72293E77B5001665B1 /* Utilities.swift in Sources */,
|
E1575E72293E77B5001665B1 /* Utilities.swift in Sources */,
|
||||||
E1575E84293E7A00001665B1 /* PrimaryAppIcon.swift in Sources */,
|
E1575E84293E7A00001665B1 /* PrimaryAppIcon.swift in Sources */,
|
||||||
|
E1153DCD2BBB633B00424D36 /* FastSVGView.swift in Sources */,
|
||||||
E1E6C45129B104850064123F /* Button.swift in Sources */,
|
E1E6C45129B104850064123F /* Button.swift in Sources */,
|
||||||
E1DC981A296DD1CD00982F06 /* CinematicBackgroundView.swift in Sources */,
|
E1DC981A296DD1CD00982F06 /* CinematicBackgroundView.swift in Sources */,
|
||||||
E1A1528B28FD22F600600579 /* TextPairView.swift in Sources */,
|
E1A1528B28FD22F600600579 /* TextPairView.swift in Sources */,
|
||||||
|
@ -3678,6 +3690,7 @@
|
||||||
E139CC1D28EC836F00688DE2 /* ChapterOverlay.swift in Sources */,
|
E139CC1D28EC836F00688DE2 /* ChapterOverlay.swift in Sources */,
|
||||||
E168BD14289A4162001A6922 /* LatestInLibraryView.swift in Sources */,
|
E168BD14289A4162001A6922 /* LatestInLibraryView.swift in Sources */,
|
||||||
E1E6C45029B104840064123F /* Button.swift in Sources */,
|
E1E6C45029B104840064123F /* Button.swift in Sources */,
|
||||||
|
E1153DCC2BBB633B00424D36 /* FastSVGView.swift in Sources */,
|
||||||
E1E5D5492783CDD700692DFE /* VideoPlayerSettingsView.swift in Sources */,
|
E1E5D5492783CDD700692DFE /* VideoPlayerSettingsView.swift in Sources */,
|
||||||
E14EDEC52B8FB64E000F00A4 /* AnyItemFilter.swift in Sources */,
|
E14EDEC52B8FB64E000F00A4 /* AnyItemFilter.swift in Sources */,
|
||||||
E11245B728D97ED200D8A977 /* TopBarView.swift in Sources */,
|
E11245B728D97ED200D8A977 /* TopBarView.swift in Sources */,
|
||||||
|
@ -4398,6 +4411,14 @@
|
||||||
kind = branch;
|
kind = branch;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
E1153DCE2BBB634F00424D36 /* XCRemoteSwiftPackageReference "SVGKit" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/SVGKit/SVGKit";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 3.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
E13DD3C42716499E009D4DAF /* XCRemoteSwiftPackageReference "CoreStore" */ = {
|
E13DD3C42716499E009D4DAF /* XCRemoteSwiftPackageReference "CoreStore" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/JohnEstropia/CoreStore.git";
|
repositoryURL = "https://github.com/JohnEstropia/CoreStore.git";
|
||||||
|
@ -4595,6 +4616,16 @@
|
||||||
package = E1153DAD2BBA734200424D36 /* XCRemoteSwiftPackageReference "CollectionHStack" */;
|
package = E1153DAD2BBA734200424D36 /* XCRemoteSwiftPackageReference "CollectionHStack" */;
|
||||||
productName = CollectionHStack;
|
productName = CollectionHStack;
|
||||||
};
|
};
|
||||||
|
E1153DCF2BBB634F00424D36 /* SVGKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = E1153DCE2BBB634F00424D36 /* XCRemoteSwiftPackageReference "SVGKit" */;
|
||||||
|
productName = SVGKit;
|
||||||
|
};
|
||||||
|
E1153DD12BBB649C00424D36 /* SVGKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = E1153DCE2BBB634F00424D36 /* XCRemoteSwiftPackageReference "SVGKit" */;
|
||||||
|
productName = SVGKit;
|
||||||
|
};
|
||||||
E12186DD2718F1C50010884C /* Defaults */ = {
|
E12186DD2718F1C50010884C /* Defaults */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = E13DD3D127168E65009D4DAF /* XCRemoteSwiftPackageReference "Defaults" */;
|
package = E13DD3D127168E65009D4DAF /* XCRemoteSwiftPackageReference "Defaults" */;
|
||||||
|
|
|
@ -9,6 +9,15 @@
|
||||||
"version" : "1.2.0"
|
"version" : "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "cocoalumberjack",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "4b8714a7fb84d42393314ce897127b3939885ec3",
|
||||||
|
"version" : "3.8.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "collectionhstack",
|
"identity" : "collectionhstack",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
@ -135,6 +144,15 @@
|
||||||
"revision" : "6dda57096e16020342b36ebea86dc4bdf6783426"
|
"revision" : "6dda57096e16020342b36ebea86dc4bdf6783426"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "svgkit",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/SVGKit/SVGKit",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "58152b9f7c85eab239160b36ffdfd364aa43d666",
|
||||||
|
"version" : "3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-algorithms",
|
"identity" : "swift-algorithms",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|
Loading…
Reference in New Issue