mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Opens Error Log when tapping ToastView
This commit is contained in:
@@ -510,7 +510,7 @@ extension AppViewController
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let toastView = ToastView(error: error)
|
let toastView = ToastView(error: error, opensLog: true)
|
||||||
toastView.show(in: self)
|
toastView.show(in: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ private extension BrowseViewController
|
|||||||
{
|
{
|
||||||
case .failure(OperationError.cancelled): break // Ignore
|
case .failure(OperationError.cancelled): break // Ignore
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = ToastView(error: error)
|
let toastView = ToastView(error: error, opensLog: true)
|
||||||
toastView.show(in: self)
|
toastView.show(in: self)
|
||||||
|
|
||||||
case .success: print("Installed app:", app.bundleIdentifier)
|
case .success: print("Installed app:", app.bundleIdentifier)
|
||||||
|
|||||||
@@ -18,8 +18,17 @@ extension TimeInterval
|
|||||||
|
|
||||||
final class ToastView: RSTToastView
|
final class ToastView: RSTToastView
|
||||||
{
|
{
|
||||||
|
static let openErrorLogNotification = Notification.Name("ALTOpenErrorLogNotification")
|
||||||
|
|
||||||
var preferredDuration: TimeInterval
|
var preferredDuration: TimeInterval
|
||||||
|
|
||||||
|
var opensErrorLog: Bool = false
|
||||||
|
|
||||||
|
convenience init(text: String, detailText: String?, opensLog: Bool = false) {
|
||||||
|
self.init(text: text, detailText: detailText)
|
||||||
|
self.opensErrorLog = opensLog
|
||||||
|
}
|
||||||
|
|
||||||
override init(text: String, detailText detailedText: String?)
|
override init(text: String, detailText detailedText: String?)
|
||||||
{
|
{
|
||||||
if detailedText == nil
|
if detailedText == nil
|
||||||
@@ -43,7 +52,14 @@ final class ToastView: RSTToastView
|
|||||||
// RSTToastView does not expose stack view containing labels,
|
// RSTToastView does not expose stack view containing labels,
|
||||||
// so we access it indirectly as the labels' superview.
|
// so we access it indirectly as the labels' superview.
|
||||||
stackView.spacing = (detailedText != nil) ? 4.0 : 0.0
|
stackView.spacing = (detailedText != nil) ? 4.0 : 0.0
|
||||||
|
stackView.alignment = .leading
|
||||||
}
|
}
|
||||||
|
self.addTarget(self, action: #selector(ToastView.showErrorLog), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(error: Error, opensLog: Bool = false) {
|
||||||
|
self.init(error: error)
|
||||||
|
self.opensErrorLog = opensLog
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(error: Error)
|
convenience init(error: Error)
|
||||||
@@ -95,6 +111,18 @@ final class ToastView: RSTToastView
|
|||||||
|
|
||||||
override func show(in view: UIView, duration: TimeInterval)
|
override func show(in view: UIView, duration: TimeInterval)
|
||||||
{
|
{
|
||||||
|
if opensErrorLog, #available(iOS 13.0, *), case let configuration = UIImage.SymbolConfiguration(font: self.textLabel.font),
|
||||||
|
let icon = UIImage(systemName: "chevron.right.circle", withConfiguration: configuration) {
|
||||||
|
let tintedIcon = icon.withTintColor(.white, renderingMode: .alwaysOriginal)
|
||||||
|
let moreIconImageView = UIImageView(image: tintedIcon)
|
||||||
|
moreIconImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
self.addSubview(moreIconImageView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
moreIconImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -self.layoutMargins.right),
|
||||||
|
moreIconImageView.centerYAnchor.constraint(equalTo: self.textLabel.centerYAnchor),
|
||||||
|
moreIconImageView.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: self.textLabel.trailingAnchor, multiplier: 1.0)
|
||||||
|
])
|
||||||
|
}
|
||||||
super.show(in: view, duration: duration)
|
super.show(in: view, duration: duration)
|
||||||
|
|
||||||
let announcement = (self.textLabel.text ?? "") + ". " + (self.detailTextLabel.text ?? "")
|
let announcement = (self.textLabel.text ?? "") + ". " + (self.detailTextLabel.text ?? "")
|
||||||
@@ -110,4 +138,10 @@ final class ToastView: RSTToastView
|
|||||||
{
|
{
|
||||||
self.show(in: view, duration: self.preferredDuration)
|
self.show(in: view, duration: self.preferredDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func showErrorLog() {
|
||||||
|
guard self.opensErrorLog else { return }
|
||||||
|
NotificationCenter.default.post(name: ToastView.openErrorLogNotification, object: self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -535,11 +535,9 @@ private extension MyAppsViewController
|
|||||||
|
|
||||||
guard !failures.isEmpty else { return }
|
guard !failures.isEmpty else { return }
|
||||||
|
|
||||||
let toastView: ToastView
|
|
||||||
|
|
||||||
if let failure = failures.first, results.count == 1
|
if let failure = failures.first, results.count == 1
|
||||||
{
|
{
|
||||||
toastView = ToastView(error: failure.value)
|
ToastView(error: failure.value).show(in: self)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -557,12 +555,11 @@ private extension MyAppsViewController
|
|||||||
let error = failures.first?.value as NSError?
|
let error = failures.first?.value as NSError?
|
||||||
let detailText = error?.localizedFailure ?? error?.localizedFailureReason ?? error?.localizedDescription
|
let detailText = error?.localizedFailure ?? error?.localizedFailureReason ?? error?.localizedDescription
|
||||||
|
|
||||||
toastView = ToastView(text: localizedText, detailText: detailText)
|
let toastView = ToastView(text: localizedText, detailText: detailText, opensLog: true)
|
||||||
toastView.preferredDuration = 4.0
|
toastView.preferredDuration = 4.0
|
||||||
}
|
|
||||||
|
|
||||||
toastView.show(in: self)
|
toastView.show(in: self)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.refreshGroup = nil
|
self.refreshGroup = nil
|
||||||
completionHandler(results)
|
completionHandler(results)
|
||||||
@@ -697,8 +694,7 @@ private extension MyAppsViewController
|
|||||||
self.collectionView.reloadItems(at: [indexPath])
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
|
|
||||||
self.collectionView.reloadItems(at: [indexPath])
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
|
||||||
@@ -899,8 +895,7 @@ private extension MyAppsViewController
|
|||||||
completion(.failure((OperationError.cancelled)))
|
completion(.failure((OperationError.cancelled)))
|
||||||
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
|
|
||||||
completion(.failure(error))
|
completion(.failure(error))
|
||||||
}
|
}
|
||||||
@@ -1015,8 +1010,7 @@ private extension MyAppsViewController
|
|||||||
UIApplication.shared.open(installedApp.openAppURL) { success in
|
UIApplication.shared.open(installedApp.openAppURL) { success in
|
||||||
guard !success else { return }
|
guard !success else { return }
|
||||||
|
|
||||||
let toastView = ToastView(error: OperationError.openAppFailed(name: installedApp.name))
|
ToastView(error: OperationError.openAppFailed(name: installedApp.name), opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1067,8 +1061,7 @@ private extension MyAppsViewController
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
installedApp.isActive = false
|
installedApp.isActive = false
|
||||||
|
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1141,8 +1134,7 @@ private extension MyAppsViewController
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
installedApp.isActive = true
|
installedApp.isActive = true
|
||||||
|
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1173,8 +1165,7 @@ private extension MyAppsViewController
|
|||||||
case .success: break
|
case .success: break
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1208,8 +1199,7 @@ private extension MyAppsViewController
|
|||||||
print("Failed to back up app:", error)
|
print("Failed to back up app:", error)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
|
|
||||||
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
|
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
|
||||||
}
|
}
|
||||||
@@ -1245,8 +1235,7 @@ private extension MyAppsViewController
|
|||||||
print("Failed to restore app:", error)
|
print("Failed to restore app:", error)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1321,8 +1310,7 @@ private extension MyAppsViewController
|
|||||||
print("Failed to change app icon.", error)
|
print("Failed to change app icon.", error)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1334,9 +1322,8 @@ private extension MyAppsViewController
|
|||||||
guard minimuxerStatus else { return }
|
guard minimuxerStatus else { return }
|
||||||
|
|
||||||
if #available(iOS 17, *) {
|
if #available(iOS 17, *) {
|
||||||
let toastView = ToastView(error: (OperationError.tooNewError as NSError).withLocalizedTitle("No iOS 17 On Device JIT!"))
|
ToastView(error: (OperationError.tooNewError as NSError).withLocalizedTitle("No iOS 17 On Device JIT!"), opensLog: true).show(in: self)
|
||||||
AppManager.shared.log(OperationError.tooNewError, operation: .enableJIT, app: installedApp)
|
AppManager.shared.log(OperationError.tooNewError, operation: .enableJIT, app: installedApp)
|
||||||
toastView.show(in: self)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1346,8 +1333,8 @@ private extension MyAppsViewController
|
|||||||
{
|
{
|
||||||
case .success: break
|
case .success: break
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 5)
|
AppManager.shared.log(error, operation: .enableJIT, app: installedApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,8 +313,7 @@ private extension NewsViewController
|
|||||||
{
|
{
|
||||||
case .failure(OperationError.cancelled): break // Ignore
|
case .failure(OperationError.cancelled): break // Ignore
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = ToastView(error: error)
|
ToastView(error: error, opensLog: true).show(in: self)
|
||||||
toastView.show(in: self)
|
|
||||||
|
|
||||||
case .success: print("Installed app:", storeApp.bundleIdentifier)
|
case .success: print("Installed app:", storeApp.bundleIdentifier)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2">
|
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2">
|
||||||
<rect key="frame" x="0.0" y="1296" width="375" height="25"/>
|
<rect key="frame" x="0.0" y="1209" width="375" height="25"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
@@ -618,7 +618,7 @@
|
|||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="g8a-Rf-zWa" kind="show" id="vFC-Id-Ww6"/>
|
<segue destination="g8a-Rf-zWa" kind="show" identifier="showErrorLog" id="vFC-Id-Ww6"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||||
@@ -1210,7 +1210,7 @@ Settings by i cons from the Noun Project</string>
|
|||||||
<!--Error Details View Controller-->
|
<!--Error Details View Controller-->
|
||||||
<scene sceneID="XNO-Yg-I7t">
|
<scene sceneID="XNO-Yg-I7t">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="xB2-Se-VVg" customClass="ErrorDetailsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="xB2-Se-VVg" customClass="ErrorDetailsViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="eBQ-se-VIy">
|
<view key="view" contentMode="scaleToFill" id="eBQ-se-VIy">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ final class SettingsViewController: UITableViewController
|
|||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.openErrorLog(_:)), name: ToastView.openErrorLogNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
@@ -109,16 +110,13 @@ final class SettingsViewController: UITableViewController
|
|||||||
debugModeGestureRecognizer.numberOfTouchesRequired = 3
|
debugModeGestureRecognizer.numberOfTouchesRequired = 3
|
||||||
self.tableView.addGestureRecognizer(debugModeGestureRecognizer)
|
self.tableView.addGestureRecognizer(debugModeGestureRecognizer)
|
||||||
|
|
||||||
print(Bundle.main.infoDictionary)
|
|
||||||
var versionString: String = ""
|
var versionString: String = ""
|
||||||
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||||||
{
|
{
|
||||||
versionString += "SideStore \(version)"
|
versionString += "SideStore \(version)"
|
||||||
if let xcode = Bundle.main.object(forInfoDictionaryKey: "DTXcode") as? String {
|
if let xcode = Bundle.main.object(forInfoDictionaryKey: "DTXcode") as? String {
|
||||||
print(xcode)
|
|
||||||
versionString += " - Xcode \(xcode) - "
|
versionString += " - Xcode \(xcode) - "
|
||||||
if let build = Bundle.main.object(forInfoDictionaryKey: "DTXcodeBuild") as? String {
|
if let build = Bundle.main.object(forInfoDictionaryKey: "DTXcodeBuild") as? String {
|
||||||
print(build)
|
|
||||||
versionString += "\(build)"
|
versionString += "\(build)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,6 +406,15 @@ private extension SettingsViewController
|
|||||||
self.performSegue(withIdentifier: "showPatreon", sender: nil)
|
self.performSegue(withIdentifier: "showPatreon", sender: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func openErrorLog(_: Notification) {
|
||||||
|
guard self.presentedViewController == nil else { return }
|
||||||
|
|
||||||
|
self.navigationController?.popViewController(animated: false)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.performSegue(withIdentifier: "showErrorLog", sender: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SettingsViewController
|
extension SettingsViewController
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ final class TabBarController: UITabBarController
|
|||||||
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.importApp(_:)), name: AppDelegate.importAppDeepLinkNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.importApp(_:)), name: AppDelegate.importAppDeepLinkNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.presentSources(_:)), name: AppDelegate.addSourceDeepLinkNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.presentSources(_:)), name: AppDelegate.addSourceDeepLinkNotification, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openErrorLog(_:)), name: ToastView.openErrorLogNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
override func viewDidAppear(_ animated: Bool)
|
||||||
@@ -141,4 +142,7 @@ private extension TabBarController
|
|||||||
{
|
{
|
||||||
self.selectedIndex = Tab.myApps.rawValue
|
self.selectedIndex = Tab.myApps.rawValue
|
||||||
}
|
}
|
||||||
|
@objc func openErrorLog(_: Notification){
|
||||||
|
self.selectedIndex = Tab.settings.rawValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user