mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Shows app’s source icon on AppBannerView
Excluding contexts where it is redundant (e.g. source detail page).
This commit is contained in:
@@ -219,12 +219,16 @@ private extension BrowseViewController
|
||||
|
||||
let context = self.source?.managedObjectContext ?? DatabaseManager.shared.viewContext
|
||||
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<StoreApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: context)
|
||||
dataSource.cellConfigurationHandler = { (cell, app, indexPath) in
|
||||
dataSource.placeholderView = self.placeholderView
|
||||
dataSource.cellConfigurationHandler = { [weak self] (cell, app, indexPath) in
|
||||
guard let self else { return }
|
||||
|
||||
let cell = cell as! AppCardCollectionViewCell
|
||||
cell.layoutMargins.left = self.view.layoutMargins.left
|
||||
cell.layoutMargins.right = self.view.layoutMargins.right
|
||||
|
||||
cell.configure(for: app)
|
||||
let showSourceIcon = (self.source == nil) // Hide source icon if redundant
|
||||
cell.configure(for: app, showSourceIcon: showSourceIcon)
|
||||
|
||||
cell.bannerView.iconImageView.image = nil
|
||||
cell.bannerView.iconImageView.isIndicatingActivity = true
|
||||
@@ -251,19 +255,18 @@ private extension BrowseViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||
dataSource.prefetchCompletionHandler = { [weak dataSource] (cell, image, indexPath, error) in
|
||||
let cell = cell as! AppCardCollectionViewCell
|
||||
cell.bannerView.iconImageView.isIndicatingActivity = false
|
||||
cell.bannerView.iconImageView.image = image
|
||||
|
||||
if let error = error
|
||||
if let error = error, let dataSource
|
||||
{
|
||||
print("Error loading image:", error)
|
||||
let app = dataSource.item(at: indexPath)
|
||||
Logger.main.debug("Failed to load app icon from \(app.iconURL, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
dataSource.placeholderView = self.placeholderView
|
||||
|
||||
return dataSource
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import UIKit
|
||||
import AltStoreCore
|
||||
import Roxas
|
||||
|
||||
import Nuke
|
||||
|
||||
extension AppBannerView
|
||||
{
|
||||
enum Style
|
||||
@@ -65,6 +67,7 @@ class AppBannerView: RSTNibView
|
||||
@IBOutlet var button: PillButton!
|
||||
@IBOutlet var buttonLabel: UILabel!
|
||||
@IBOutlet var betaBadgeView: UIView!
|
||||
@IBOutlet var sourceIconImageView: AppIconImageView!
|
||||
|
||||
@IBOutlet var backgroundEffectView: UIVisualEffectView!
|
||||
|
||||
@@ -97,6 +100,9 @@ class AppBannerView: RSTNibView
|
||||
|
||||
self.betaBadgeView.isHidden = true
|
||||
|
||||
self.sourceIconImageView.style = .circular
|
||||
self.sourceIconImageView.isHidden = true
|
||||
|
||||
self.layoutMargins = self.stackView.layoutMargins
|
||||
self.insetsLayoutMarginsFromSafeArea = false
|
||||
|
||||
@@ -119,7 +125,7 @@ class AppBannerView: RSTNibView
|
||||
|
||||
extension AppBannerView
|
||||
{
|
||||
func configure(for app: AppProtocol, action: AppAction? = nil)
|
||||
func configure(for app: AppProtocol, action: AppAction? = nil, showSourceIcon: Bool = true)
|
||||
{
|
||||
struct AppValues
|
||||
{
|
||||
@@ -170,6 +176,37 @@ extension AppBannerView
|
||||
self.buttonLabel.isHidden = true
|
||||
}
|
||||
|
||||
if let source = app.storeApp?.source, showSourceIcon
|
||||
{
|
||||
self.sourceIconImageView.isHidden = false
|
||||
self.sourceIconImageView.backgroundColor = source.effectiveTintColor?.adjustedForDisplay ?? .altPrimary
|
||||
|
||||
if let iconURL = source.effectiveIconURL
|
||||
{
|
||||
if let image = ImageCache.shared[iconURL]
|
||||
{
|
||||
self.sourceIconImageView.backgroundColor = .white
|
||||
self.sourceIconImageView.image = image.image
|
||||
}
|
||||
else
|
||||
{
|
||||
self.sourceIconImageView.image = nil
|
||||
|
||||
Nuke.loadImage(with: iconURL, into: self.sourceIconImageView) { result in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): Logger.main.error("Failed to fetch source icon from \(iconURL, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
case .success: self.sourceIconImageView.backgroundColor = .white // In case icon has transparent background.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.sourceIconImageView.isHidden = true
|
||||
}
|
||||
|
||||
let buttonAction: AppAction
|
||||
|
||||
if let action
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<outlet property="buttonLabel" destination="Yd9-jw-faD" id="o7g-Gb-CIt"/>
|
||||
<outlet property="iconImageView" destination="avS-dx-4iy" id="TQs-Ej-gin"/>
|
||||
<outlet property="iconImageViewHeightConstraint" destination="6lU-H8-nEw" id="PSt-Xa-lQT"/>
|
||||
<outlet property="sourceIconImageView" destination="dku-SJ-aay" id="rA0-y1-dIb"/>
|
||||
<outlet property="stackView" destination="d1T-UD-gWG" id="E7N-Zb-lm1"/>
|
||||
<outlet property="subtitleLabel" destination="oN5-vu-Dnw" id="gA4-iJ-Tix"/>
|
||||
<outlet property="titleLabel" destination="mFe-zJ-eva" id="2OH-f8-cid"/>
|
||||
@@ -58,18 +59,25 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="100" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" alignment="top" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="caL-vN-Svn">
|
||||
<rect key="frame" x="87" y="25.5" width="184" height="37.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="500" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="1Es-pv-zwd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="126" height="19.5"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="500" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="1Es-pv-zwd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="149" height="19.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" horizontalCompressionResistancePriority="500" text="App Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="mFe-zJ-eva">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="400" verticalHuggingPriority="251" horizontalCompressionResistancePriority="500" text="App Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="mFe-zJ-eva">
|
||||
<rect key="frame" x="0.0" y="0.0" width="79" height="19.5"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="NameLabel"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dku-SJ-aay" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
|
||||
<rect key="frame" x="85" y="1" width="17" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="dku-SJ-aay" secondAttribute="height" id="VKw-lc-8NQ"/>
|
||||
<constraint firstAttribute="width" constant="17" id="hAe-gc-Ehh"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="BetaBadge" translatesAutoresizingMaskIntoConstraints="NO" id="qQl-Ez-zC5">
|
||||
<rect key="frame" x="85" y="0.0" width="41" height="19.5"/>
|
||||
<rect key="frame" x="108" y="1" width="41" height="17"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="Beta Badge">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
|
||||
@@ -285,7 +285,7 @@ private extension AppCardCollectionViewCell
|
||||
|
||||
extension AppCardCollectionViewCell
|
||||
{
|
||||
func configure(for storeApp: StoreApp)
|
||||
func configure(for storeApp: StoreApp, showSourceIcon: Bool = true)
|
||||
{
|
||||
self.screenshots = storeApp.preferredScreenshots()
|
||||
|
||||
@@ -294,7 +294,7 @@ extension AppCardCollectionViewCell
|
||||
self.bannerView.button.isIndicatingActivity = false
|
||||
|
||||
self.bannerView.tintColor = storeApp.tintColor
|
||||
self.bannerView.configure(for: storeApp)
|
||||
self.bannerView.configure(for: storeApp, showSourceIcon: showSourceIcon)
|
||||
|
||||
self.bannerView.subtitleLabel.numberOfLines = 1
|
||||
self.bannerView.subtitleLabel.lineBreakMode = .byTruncatingTail
|
||||
|
||||
@@ -226,11 +226,12 @@ private extension SourceDetailContentViewController
|
||||
cell.contentView.backgroundColor = .altBackground
|
||||
|
||||
cell.bannerView.button.isIndicatingActivity = false
|
||||
cell.bannerView.configure(for: storeApp)
|
||||
cell.bannerView.configure(for: storeApp, showSourceIcon: false)
|
||||
|
||||
cell.bannerView.button.tintColor = storeApp.tintColor
|
||||
cell.bannerView.button.addTarget(self, action: #selector(SourceDetailContentViewController.performAppAction(_:)), for: .primaryActionTriggered)
|
||||
|
||||
cell.bannerView.iconImageView.image = nil
|
||||
cell.bannerView.iconImageView.isIndicatingActivity = true
|
||||
}
|
||||
dataSource.prefetchHandler = { (storeApp, indexPath, completion) -> Foundation.Operation? in
|
||||
@@ -248,14 +249,15 @@ private extension SourceDetailContentViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||
dataSource.prefetchCompletionHandler = { [weak dataSource] (cell, image, indexPath, error) in
|
||||
let cell = cell as! AppBannerCollectionViewCell
|
||||
cell.bannerView.iconImageView.image = image
|
||||
cell.bannerView.iconImageView.isIndicatingActivity = false
|
||||
|
||||
if let error
|
||||
if let error, let dataSource
|
||||
{
|
||||
print("[ALTLog] Error loading source icon:", error)
|
||||
let app = dataSource.item(at: indexPath)
|
||||
Logger.main.debug("Failed to fetch app icon from \(app.iconURL, privacy: .public). \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user