mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
Shows source errors in SourcesViewController
This commit is contained in:
@@ -127,6 +127,7 @@
|
||||
BF4C7F2523801F0800B2556E /* AltSign.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF9B63C5229DD44D002F0A62 /* AltSign.framework */; };
|
||||
BF4E8456246F16D700ECCBD4 /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; };
|
||||
BF54E8212315EF0D000AE0D8 /* ALTPatreonBenefitType.m in Sources */ = {isa = PBXBuildFile; fileRef = BF54E8202315EF0D000AE0D8 /* ALTPatreonBenefitType.m */; };
|
||||
BF56333824EC5E9A00038F00 /* SecureValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56333724EC5E9A00038F00 /* SecureValueTransformer.swift */; };
|
||||
BF56D2AA23DF88310006506D /* AppID.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2A923DF88310006506D /* AppID.swift */; };
|
||||
BF56D2AC23DF8E170006506D /* FetchAppIDsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */; };
|
||||
BF56D2AF23DF9E310006506D /* AppIDsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56D2AE23DF9E310006506D /* AppIDsViewController.swift */; };
|
||||
@@ -136,11 +137,11 @@
|
||||
BF58048A246A28F9008AE704 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF580488246A28F9008AE704 /* LaunchScreen.storyboard */; };
|
||||
BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF580495246A3CB5008AE704 /* UIColor+AltBackup.swift */; };
|
||||
BF580498246A3D19008AE704 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF580497246A3D19008AE704 /* UIKit.framework */; };
|
||||
BF58049B246A432D008AE704 /* NSError+LocalizedFailure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */; };
|
||||
BF58049B246A432D008AE704 /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; };
|
||||
BF5C5FCF237DF69100EDD0C6 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
||||
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */; };
|
||||
BF6A5320246DC1B0004F59C8 /* FileManager+SharedDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */; };
|
||||
BF6C336224197D700034FD24 /* NSError+LocalizedFailure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */; };
|
||||
BF6C336224197D700034FD24 /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; };
|
||||
BF6C33652419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF6C33642419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel */; };
|
||||
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */ = {isa = PBXBuildFile; fileRef = BF6C8FAA242935ED00125131 /* NSAttributedString+Markdown.m */; };
|
||||
BF6C8FAE2429597900125131 /* BannerCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C8FAD2429597900125131 /* BannerCollectionViewCell.swift */; };
|
||||
@@ -479,6 +480,7 @@
|
||||
BF4713A422976CFC00784A2F /* openssl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = openssl.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF54E81F2315EF0D000AE0D8 /* ALTPatreonBenefitType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTPatreonBenefitType.h; sourceTree = "<group>"; };
|
||||
BF54E8202315EF0D000AE0D8 /* ALTPatreonBenefitType.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPatreonBenefitType.m; sourceTree = "<group>"; };
|
||||
BF56333724EC5E9A00038F00 /* SecureValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureValueTransformer.swift; sourceTree = "<group>"; };
|
||||
BF56D2A823DF87570006506D /* AltStore 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BF56D2A923DF88310006506D /* AppID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppID.swift; sourceTree = "<group>"; };
|
||||
BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAppIDsOperation.swift; sourceTree = "<group>"; };
|
||||
@@ -499,7 +501,7 @@
|
||||
BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPluginService.m; sourceTree = "<group>"; };
|
||||
BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+DirectorySize.swift"; sourceTree = "<group>"; };
|
||||
BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+SharedDirectories.swift"; sourceTree = "<group>"; };
|
||||
BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+LocalizedFailure.swift"; sourceTree = "<group>"; };
|
||||
BF6C336124197D700034FD24 /* NSError+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+AltStore.swift"; sourceTree = "<group>"; };
|
||||
BF6C33632419ADEB0034FD24 /* AltStore 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BF6C33642419AE310034FD24 /* AltStore4ToAltStore5.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore4ToAltStore5.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BF6C8FAA242935ED00125131 /* NSAttributedString+Markdown.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+Markdown.m"; path = "Dependencies/MarkdownAttributedString/NSAttributedString+Markdown.m"; sourceTree = SOURCE_ROOT; };
|
||||
@@ -1266,6 +1268,7 @@
|
||||
BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */,
|
||||
BFB11691229322E400BB457C /* DatabaseManager.swift */,
|
||||
BF3D64A122E8031100E9056B /* MergePolicy.swift */,
|
||||
BF56333724EC5E9A00038F00 /* SecureValueTransformer.swift */,
|
||||
BFE6326722A858F300F30809 /* Account.swift */,
|
||||
BF56D2A923DF88310006506D /* AppID.swift */,
|
||||
BF3D648722E79A3700E9056B /* AppPermission.swift */,
|
||||
@@ -1291,7 +1294,7 @@
|
||||
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */,
|
||||
BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */,
|
||||
BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */,
|
||||
BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */,
|
||||
BF6C336124197D700034FD24 /* NSError+AltStore.swift */,
|
||||
BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */,
|
||||
BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */,
|
||||
BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */,
|
||||
@@ -2005,7 +2008,7 @@
|
||||
BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */,
|
||||
BF580482246A28F7008AE704 /* ViewController.swift in Sources */,
|
||||
BF44EEF0246B08BA002A52F2 /* BackupController.swift in Sources */,
|
||||
BF58049B246A432D008AE704 /* NSError+LocalizedFailure.swift in Sources */,
|
||||
BF58049B246A432D008AE704 /* NSError+AltStore.swift in Sources */,
|
||||
BF58047E246A28F7008AE704 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -2024,6 +2027,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFDB6A0F22AB2776007EA6D6 /* SendAppOperation.swift in Sources */,
|
||||
BF56333824EC5E9A00038F00 /* SecureValueTransformer.swift in Sources */,
|
||||
BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */,
|
||||
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */,
|
||||
BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */,
|
||||
@@ -2061,7 +2065,7 @@
|
||||
BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */,
|
||||
BFD5D6EA230CCAE5007955AB /* PatreonAccount.swift in Sources */,
|
||||
BFE6326822A858F300F30809 /* Account.swift in Sources */,
|
||||
BF6C336224197D700034FD24 /* NSError+LocalizedFailure.swift in Sources */,
|
||||
BF6C336224197D700034FD24 /* NSError+AltStore.swift in Sources */,
|
||||
BFE6326622A857C200F30809 /* Team.swift in Sources */,
|
||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */,
|
||||
BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */,
|
||||
|
||||
@@ -77,6 +77,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
ServerManager.shared.startDiscovering()
|
||||
|
||||
SecureValueTransformer.register()
|
||||
|
||||
UserDefaults.standard.registerDefaults()
|
||||
|
||||
if UserDefaults.standard.firstLaunch == nil
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="wKh-xq-NuP">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="wKh-xq-NuP">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
@@ -38,6 +38,7 @@
|
||||
<segue destination="faz-B4-Sub" kind="relationship" relationship="viewControllers" id="dXz-Tu-hW8"/>
|
||||
<segue destination="3Ew-ox-i4n" kind="relationship" relationship="viewControllers" id="zii-dF-qEt"/>
|
||||
<segue destination="p3d-dP-Swg" kind="relationship" relationship="viewControllers" id="4Nf-rL-P4c"/>
|
||||
<segue destination="Qo4-72-Hmr" kind="presentation" identifier="presentSources" id="Qd6-ba-dIo"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
||||
@@ -1004,7 +1005,7 @@ World</string>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" style="done" systemItem="done" id="NQF-u2-PZv">
|
||||
<connections>
|
||||
<segue destination="zjS-Nr-VTw" kind="unwind" unwindAction="unwindToBrowseViewController:" id="VFy-hV-mNV"/>
|
||||
<segue destination="zjS-Nr-VTw" kind="unwind" unwindAction="unwindFromSourcesViewController:" id="la1-dJ-UhL"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
@@ -1034,6 +1035,7 @@ World</string>
|
||||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="Qd6-ba-dIo"/>
|
||||
<segue reference="cnd-KK-o60"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
<color key="tintColor" name="Primary"/>
|
||||
@@ -1044,7 +1046,7 @@ World</string>
|
||||
<image name="News" width="19" height="20"/>
|
||||
<image name="Settings" width="20" height="20"/>
|
||||
<namedColor name="Background">
|
||||
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.0040000001899898052" green="0.50199997425079346" blue="0.51800000667572021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<namedColor name="BlurTint">
|
||||
<color red="1" green="1" blue="1" alpha="0.30000001192092896" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
||||
@@ -59,7 +59,7 @@ class BrowseViewController: UICollectionViewController
|
||||
self.updateDataSource()
|
||||
}
|
||||
|
||||
@IBAction private func unwindToBrowseViewController(_ segue: UIStoryboardSegue)
|
||||
@IBAction private func unwindFromSourcesViewController(_ segue: UIStoryboardSegue)
|
||||
{
|
||||
self.fetchSource()
|
||||
}
|
||||
@@ -199,6 +199,7 @@ private extension BrowseViewController
|
||||
if self.dataSource.itemCount > 0
|
||||
{
|
||||
let toastView = ToastView(error: error)
|
||||
toastView.addTarget(nil, action: #selector(TabBarController.presentSources), for: .touchUpInside)
|
||||
toastView.show(in: self)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ import UIKit
|
||||
|
||||
class BannerCollectionViewCell: UICollectionViewCell
|
||||
{
|
||||
@IBOutlet var bannerView: AppBannerView!
|
||||
private(set) var errorBadge: UIView?
|
||||
@IBOutlet private(set) var bannerView: AppBannerView!
|
||||
|
||||
override func awakeFromNib()
|
||||
{
|
||||
@@ -18,5 +19,36 @@ class BannerCollectionViewCell: UICollectionViewCell
|
||||
|
||||
self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
self.contentView.preservesSuperviewLayoutMargins = true
|
||||
|
||||
if #available(iOS 13.0, *)
|
||||
{
|
||||
let errorBadge = UIView()
|
||||
errorBadge.translatesAutoresizingMaskIntoConstraints = false
|
||||
errorBadge.isHidden = true
|
||||
self.addSubview(errorBadge)
|
||||
|
||||
// Solid background to make the X opaque white.
|
||||
let backgroundView = UIView()
|
||||
backgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
backgroundView.backgroundColor = .white
|
||||
errorBadge.addSubview(backgroundView)
|
||||
|
||||
let badgeView = UIImageView(image: UIImage(systemName: "exclamationmark.circle.fill"))
|
||||
badgeView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(scale: .large)
|
||||
badgeView.tintColor = .systemRed
|
||||
errorBadge.addSubview(badgeView, pinningEdgesWith: .zero)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
errorBadge.centerXAnchor.constraint(equalTo: self.bannerView.trailingAnchor, constant: -5),
|
||||
errorBadge.centerYAnchor.constraint(equalTo: self.bannerView.topAnchor, constant: 5),
|
||||
|
||||
backgroundView.centerXAnchor.constraint(equalTo: badgeView.centerXAnchor),
|
||||
backgroundView.centerYAnchor.constraint(equalTo: badgeView.centerYAnchor),
|
||||
backgroundView.widthAnchor.constraint(equalTo: badgeView.widthAnchor, multiplier: 0.5),
|
||||
backgroundView.heightAnchor.constraint(equalTo: badgeView.heightAnchor, multiplier: 0.5)
|
||||
])
|
||||
|
||||
self.errorBadge = errorBadge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// NSError+LocalizedFailure.swift
|
||||
// NSError+AltStore.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 3/11/20.
|
||||
@@ -27,6 +27,21 @@ extension NSError
|
||||
let error = NSError(domain: self.domain, code: self.code, userInfo: userInfo)
|
||||
return error
|
||||
}
|
||||
|
||||
func sanitizedForCoreData() -> NSError
|
||||
{
|
||||
var userInfo = self.userInfo
|
||||
userInfo[NSLocalizedFailureErrorKey] = self.localizedFailure
|
||||
userInfo[NSLocalizedDescriptionKey] = self.localizedDescription
|
||||
userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason
|
||||
userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion
|
||||
|
||||
// Remove non-ObjC-compliant userInfo values.
|
||||
userInfo["NSCodingPath"] = nil
|
||||
|
||||
let error = NSError(domain: self.domain, code: self.code, userInfo: userInfo)
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
protocol ALTLocalizedError: LocalizedError, CustomNSError
|
||||
@@ -220,6 +220,7 @@ extension AppManager
|
||||
case .success(let source): fetchedSources.insert(source)
|
||||
case .failure(let error):
|
||||
let source = managedObjectContext.object(with: source.objectID) as! Source
|
||||
source.error = (error as NSError).sanitizedForCoreData()
|
||||
errors[source] = error
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16117.1" systemVersion="19D76" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" systemVersion="19G73" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Account" representedClassName="Account" syncable="YES">
|
||||
<attribute name="appleID" attributeType="String"/>
|
||||
<attribute name="firstName" attributeType="String"/>
|
||||
@@ -104,6 +104,7 @@
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Source" representedClassName="Source" syncable="YES">
|
||||
<attribute name="error" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="sourceURL" attributeType="URI"/>
|
||||
@@ -166,7 +167,7 @@
|
||||
<element name="NewsItem" positionX="-45" positionY="126" width="128" height="238"/>
|
||||
<element name="PatreonAccount" positionX="-45" positionY="117" width="128" height="105"/>
|
||||
<element name="RefreshAttempt" positionX="-45" positionY="117" width="128" height="105"/>
|
||||
<element name="Source" positionX="-45" positionY="99" width="128" height="118"/>
|
||||
<element name="Source" positionX="-45" positionY="99" width="128" height="133"/>
|
||||
<element name="StoreApp" positionX="-63" positionY="-18" width="128" height="343"/>
|
||||
<element name="Team" positionX="-45" positionY="81" width="128" height="148"/>
|
||||
</elements>
|
||||
|
||||
26
AltStore/Model/SecureValueTransformer.swift
Normal file
26
AltStore/Model/SecureValueTransformer.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// SecureValueTransformer.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 8/18/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc(ALTSecureValueTransformer)
|
||||
final class SecureValueTransformer: NSSecureUnarchiveFromDataTransformer
|
||||
{
|
||||
static let name = NSValueTransformerName(rawValue: "ALTSecureValueTransformer")
|
||||
|
||||
override static var allowedTopLevelClasses: [AnyClass] {
|
||||
let allowedClasses = super.allowedTopLevelClasses + [NSError.self]
|
||||
return allowedClasses
|
||||
}
|
||||
|
||||
public static func register()
|
||||
{
|
||||
let transformer = SecureValueTransformer()
|
||||
ValueTransformer.setValueTransformer(transformer, forName: name)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@ class Source: NSManagedObject, Fetchable, Decodable
|
||||
@NSManaged var identifier: String
|
||||
@NSManaged var sourceURL: URL
|
||||
|
||||
@NSManaged var error: NSError?
|
||||
|
||||
/* Non-Core Data Properties */
|
||||
var userInfo: [ALTSourceUserInfoKey: String]?
|
||||
|
||||
|
||||
@@ -99,6 +99,11 @@ class NewsViewController: UICollectionViewController
|
||||
self.collectionView.contentInset.bottom = 20
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func unwindFromSourcesViewController(_ segue: UIStoryboardSegue)
|
||||
{
|
||||
self.fetchSource()
|
||||
}
|
||||
}
|
||||
|
||||
private extension NewsViewController
|
||||
@@ -198,6 +203,7 @@ private extension NewsViewController
|
||||
if self.dataSource.itemCount > 0
|
||||
{
|
||||
let toastView = ToastView(error: error)
|
||||
toastView.addTarget(nil, action: #selector(TabBarController.presentSources), for: .touchUpInside)
|
||||
toastView.show(in: self)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ private extension SourcesViewController
|
||||
cell.bannerView.subtitleLabel.text = source.sourceURL.absoluteString
|
||||
cell.bannerView.subtitleLabel.numberOfLines = 2
|
||||
|
||||
cell.errorBadge?.isHidden = (source.error == nil)
|
||||
|
||||
// Make sure refresh button is correct size.
|
||||
cell.layoutIfNeeded()
|
||||
}
|
||||
@@ -98,6 +100,29 @@ private extension SourcesViewController
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func present(_ error: Error)
|
||||
{
|
||||
let nsError = error as NSError
|
||||
let message = nsError.userInfo[NSDebugDescriptionErrorKey] as? String ?? nsError.localizedRecoverySuggestion
|
||||
|
||||
let alertController = UIAlertController(title: error.localizedDescription, message: message, preferredStyle: .alert)
|
||||
alertController.addAction(.ok)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension SourcesViewController
|
||||
{
|
||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
|
||||
{
|
||||
self.collectionView.deselectItem(at: indexPath, animated: true)
|
||||
|
||||
let source = self.dataSource.item(at: indexPath)
|
||||
guard let error = source.error else { return }
|
||||
|
||||
self.present(error)
|
||||
}
|
||||
}
|
||||
|
||||
extension SourcesViewController: UICollectionViewDelegateFlowLayout
|
||||
@@ -134,9 +159,13 @@ extension SourcesViewController
|
||||
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
|
||||
{
|
||||
let source = self.dataSource.item(at: indexPath)
|
||||
guard source.identifier != Source.altStoreIdentifier else { return nil }
|
||||
|
||||
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil) { (suggestedActions) -> UIMenu? in
|
||||
let viewErrorAction = UIAction(title: NSLocalizedString("View Error", comment: ""), image: UIImage(systemName: "exclamationmark.circle")) { (action) in
|
||||
guard let error = source.error else { return }
|
||||
self.present(error)
|
||||
}
|
||||
|
||||
let deleteAction = UIAction(title: NSLocalizedString("Remove", comment: ""), image: UIImage(systemName: "trash"), attributes: [.destructive]) { (action) in
|
||||
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
@@ -153,8 +182,20 @@ extension SourcesViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var actions: [UIAction] = []
|
||||
|
||||
if source.error != nil
|
||||
{
|
||||
actions.append(viewErrorAction)
|
||||
}
|
||||
|
||||
if source.identifier != Source.altStoreIdentifier
|
||||
{
|
||||
actions.append(deleteAction)
|
||||
}
|
||||
|
||||
let menu = UIMenu(title: "", children: [deleteAction])
|
||||
let menu = UIMenu(title: "", children: actions)
|
||||
return menu
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,14 @@ class TabBarController: UITabBarController
|
||||
}
|
||||
}
|
||||
|
||||
extension TabBarController
|
||||
{
|
||||
@objc func presentSources(_ sender: Any)
|
||||
{
|
||||
self.performSegue(withIdentifier: "presentSources", sender: sender)
|
||||
}
|
||||
}
|
||||
|
||||
private extension TabBarController
|
||||
{
|
||||
@objc func openPatreonSettings(_ notification: Notification)
|
||||
|
||||
Reference in New Issue
Block a user