Files
SideStore/AltStore/Apps/AppDetailViewController.swift
Riley Testut 39c84e623a Prioritizes app refresh order
Tries to refresh apps that are about to expire first, and then always refreshes AltStore itself last, since refreshing AltStore means that the app will quit.
2019-06-21 11:20:29 -07:00

190 lines
6.0 KiB
Swift

//
// AppDetailViewController.swift
// AltStore
//
// Created by Riley Testut on 5/9/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import Roxas
@objc(ScreenshotCollectionViewCell)
private class ScreenshotCollectionViewCell: UICollectionViewCell
{
@IBOutlet var imageView: UIImageView!
}
extension AppDetailViewController
{
private enum Row: Int
{
case general
case screenshots
case description
}
}
class AppDetailViewController: UITableViewController
{
var app: App!
private lazy var screenshotsDataSource = self.makeScreenshotsDataSource()
@IBOutlet private var nameLabel: UILabel!
@IBOutlet private var developerButton: UIButton!
@IBOutlet private var appIconImageView: UIImageView!
@IBOutlet private var downloadButton: UIButton!
@IBOutlet private var screenshotsCollectionView: UICollectionView!
@IBOutlet private var descriptionLabel: UILabel!
override func viewDidLoad()
{
super.viewDidLoad()
self.tableView.delegate = self
self.screenshotsCollectionView.dataSource = self.screenshotsDataSource
self.downloadButton.activityIndicatorView.style = .white
self.update()
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.update()
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
guard let image = self.screenshotsDataSource.items.first else { return }
let aspectRatio = image.size.width / image.size.height
let height = self.screenshotsCollectionView.bounds.height
let width = self.screenshotsCollectionView.bounds.height * aspectRatio
let layout = self.screenshotsCollectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: width, height: height)
}
}
private extension AppDetailViewController
{
func update()
{
self.nameLabel.text = self.app.name
self.developerButton.setTitle(self.app.developerName, for: .normal)
self.appIconImageView.image = UIImage(named: self.app.iconName)
self.descriptionLabel.text = self.app.localizedDescription
if !self.downloadButton.isIndicatingActivity
{
if self.app.installedApp == nil
{
let text = String(format: NSLocalizedString("Download %@", comment: ""), self.app.name)
self.downloadButton.setTitle(text, for: .normal)
self.downloadButton.isEnabled = true
}
else
{
self.downloadButton.setTitle(NSLocalizedString("Installed", comment: ""), for: .normal)
self.downloadButton.isEnabled = false
}
}
}
func makeScreenshotsDataSource() -> RSTArrayCollectionViewDataSource<UIImage>
{
let screenshots = self.app.screenshotNames.compactMap(UIImage.init(named:))
let dataSource = RSTArrayCollectionViewDataSource(items: screenshots)
dataSource.cellConfigurationHandler = { (cell, screenshot, indexPath) in
let cell = cell as! ScreenshotCollectionViewCell
cell.imageView.image = screenshot
}
return dataSource
}
}
private extension AppDetailViewController
{
@IBAction func downloadApp(_ sender: UIButton)
{
guard self.app.installedApp == nil else { return }
sender.isIndicatingActivity = true
let progressView = UIProgressView(progressViewStyle: .bar)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.progress = 0.0
let progress = AppManager.shared.install(self.app, presentingViewController: self) { (result) in
do
{
_ = try result.get()
DispatchQueue.main.async {
let toastView = RSTToastView(text: "Installed \(self.app.name)!", detailText: nil)
toastView.tintColor = .altPurple
toastView.show(in: self.navigationController!.view, duration: 2)
}
}
catch OperationError.cancelled
{
// Ignore
}
catch
{
DispatchQueue.main.async {
let toastView = RSTToastView(text: "Failed to install \(self.app.name)", detailText: error.localizedDescription)
toastView.tintColor = .altPurple
toastView.show(in: self.navigationController!.view, duration: 2)
}
}
DispatchQueue.main.async {
UIView.animate(withDuration: 0.4, animations: {
progressView.alpha = 0.0
}) { _ in
progressView.removeFromSuperview()
}
sender.isIndicatingActivity = false
self.update()
}
}
progressView.observedProgress = progress
if let navigationBar = self.navigationController?.navigationBar
{
navigationBar.addSubview(progressView)
NSLayoutConstraint.activate([progressView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
}
}
}
extension AppDetailViewController
{
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
guard indexPath.row == Row.screenshots.rawValue else { return super.tableView(tableView, heightForRowAt: indexPath) }
guard !self.screenshotsDataSource.items.isEmpty else { return 0.0 }
let height = self.view.bounds.height * 0.67
return height
}
}