- Feature: Added markdown rendering for in-app update description field

This commit is contained in:
mahee96
2025-02-27 23:39:03 +05:30
parent b316e84f0d
commit 2bea980d1f
7 changed files with 418 additions and 34 deletions

View File

@@ -44,7 +44,8 @@ final class AppContentViewController: UITableViewController
@IBOutlet private var subtitleLabel: UILabel!
@IBOutlet private var descriptionTextView: CollapsingTextView!
@IBOutlet private var versionDescriptionTextView: CollapsingTextView!
// @IBOutlet private var versionDescriptionTextView: CollapsingTextView!
@IBOutlet private var versionDescriptionTextView: CollapsingMarkdownView!
@IBOutlet private var versionLabel: UILabel!
@IBOutlet private var versionDateLabel: UILabel!
@IBOutlet private var sizeLabel: UILabel!
@@ -54,9 +55,39 @@ final class AppContentViewController: UITableViewController
@IBOutlet private(set) var appDetailCollectionViewController: AppDetailCollectionViewController!
@IBOutlet private var appDetailCollectionViewHeightConstraint: NSLayoutConstraint!
//
// override func viewDidLoad()
// {
// super.viewDidLoad()
//
// self.tableView.contentInset.bottom = 20
//
// self.subtitleLabel.text = self.app.subtitle
// self.descriptionTextView.text = self.app.localizedDescription
//
// if let version = self.app.latestAvailableVersion
// {
// self.versionDescriptionTextView.text = version.localizedDescription ?? "nil"
// self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion)
// self.versionDateLabel.text = Date().relativeDateString(since: version.date)
// self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: version.size)
// }
// else
// {
// self.versionDescriptionTextView.text = "nil"
// self.versionLabel.text = nil
// self.versionDateLabel.text = nil
// self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: 0)
// }
//
// self.descriptionTextView.maximumNumberOfLines = 5
// self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
//
// self.versionDescriptionTextView.maximumNumberOfLines = 3
// self.versionDescriptionTextView.toggleButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
// }
override func viewDidLoad()
{
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.contentInset.bottom = 20
@@ -64,26 +95,32 @@ final class AppContentViewController: UITableViewController
self.subtitleLabel.text = self.app.subtitle
self.descriptionTextView.text = self.app.localizedDescription
if let version = self.app.latestAvailableVersion
{
self.versionDescriptionTextView.text = version.localizedDescription
if let version = self.app.latestAvailableVersion {
self.versionDescriptionTextView.text = version.localizedDescription ?? "nil"
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion)
self.versionDateLabel.text = Date().relativeDateString(since: version.date)
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: version.size)
}
else
{
self.versionDescriptionTextView.text = nil
self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: version.size, countStyle: .file)
} else {
self.versionDescriptionTextView.text = "nil"
self.versionLabel.text = nil
self.versionDateLabel.text = nil
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: 0)
self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: 0, countStyle: .file)
}
// Set maximum number of lines (no extra target-action needed)
self.descriptionTextView.maximumNumberOfLines = 5
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
self.versionDescriptionTextView.maximumNumberOfLines = 3
self.versionDescriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
// Instead of adding another target for toggle, set the didToggleCollapse callback:
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
self.versionDescriptionTextView.didToggleCollapse = { [weak self] in
guard let self = self else { return }
UIView.animate(withDuration: 0.25) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
}
override func viewDidLayoutSubviews()
@@ -162,14 +199,37 @@ private extension AppContentViewController
switch sender
{
case self.descriptionTextView.moreButton: indexPath = IndexPath(row: Row.description.rawValue, section: 0)
case self.versionDescriptionTextView.moreButton: indexPath = IndexPath(row: Row.versionDescription.rawValue, section: 0)
case self.descriptionTextView.moreButton:
indexPath = IndexPath(row: Row.description.rawValue, section: 0)
// Toggle the state for the text view
self.descriptionTextView.isCollapsed.toggle()
case self.versionDescriptionTextView.toggleButton:
indexPath = IndexPath(row: Row.versionDescription.rawValue, section: 0)
// Toggle the state for the markdown view
self.versionDescriptionTextView.isCollapsed.toggle()
default: return
}
// Disable animations to prevent some potentially strange ones.
UIView.performWithoutAnimation {
self.tableView.reloadRows(at: [indexPath], with: .none)
// First apply the new state to the views
switch Row.allCases[indexPath.row] {
case .description:
self.descriptionTextView.setNeedsLayout()
self.descriptionTextView.layoutIfNeeded()
case .versionDescription:
self.versionDescriptionTextView.setNeedsLayout()
self.versionDescriptionTextView.layoutIfNeeded()
default: break
}
// Then reload the row with animation
UIView.animate(withDuration: 0.25) {
// Force table view to recalculate height
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
}

View File

@@ -353,7 +353,7 @@
</stackView>
</subviews>
</stackView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="SideStore" customModuleProvider="target">
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingMarkdownView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="20" y="59.5" width="335" height="34"/>
<color key="backgroundColor" name="Background"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>

View File

@@ -235,7 +235,7 @@ private extension MyAppsViewController
cell.layoutMargins.right = self.view.layoutMargins.right
cell.tintColor = app.tintColor ?? .altPrimary
cell.versionDescriptionTextView.text = latestSupportedVersion.localizedDescription
cell.versionDescriptionTextView.text = latestSupportedVersion.localizedDescription ?? "nil"
cell.bannerView.iconImageView.image = nil
cell.bannerView.iconImageView.isIndicatingActivity = true
@@ -281,7 +281,7 @@ private extension MyAppsViewController
cell.mode = .collapsed
}
cell.versionDescriptionTextView.moreButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered)
cell.versionDescriptionTextView.toggleButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered)
cell.setNeedsLayout()
@@ -724,22 +724,29 @@ private extension MyAppsViewController
let cell = self.collectionView.cellForItem(at: indexPath) as? UpdateCollectionViewCell
// Toggle the state
if self.expandedAppUpdates.contains(installedApp.bundleIdentifier)
{
self.expandedAppUpdates.remove(installedApp.bundleIdentifier)
// Set collapsed mode on the cell
cell?.mode = .collapsed
}
else
{
self.expandedAppUpdates.insert(installedApp.bundleIdentifier)
// Set expanded mode on the cell
cell?.mode = .expanded
}
// Clear cached size so it's recalculated
self.cachedUpdateSizes[installedApp.bundleIdentifier] = nil
self.collectionView.performBatchUpdates({
self.collectionView.collectionViewLayout.invalidateLayout()
}, completion: nil)
// Animate the change smoothly with a duration
UIView.animate(withDuration: 0.25) {
self.collectionView.performBatchUpdates({
self.collectionView.collectionViewLayout.invalidateLayout()
}, completion: nil)
}
}
@IBAction func refreshApp(_ sender: UIButton)

View File

@@ -21,12 +21,19 @@ extension UpdateCollectionViewCell
{
var mode: Mode = .expanded {
didSet {
self.update()
switch self.mode {
case .collapsed:
self.versionDescriptionTextView.isCollapsed = true
case .expanded:
self.versionDescriptionTextView.isCollapsed = false
}
self.setNeedsLayout()
}
}
@IBOutlet var bannerView: AppBannerView!
@IBOutlet var versionDescriptionTextView: CollapsingTextView!
// @IBOutlet var versionDescriptionTextView: CollapsingTextView!
@IBOutlet var versionDescriptionTextView: CollapsingMarkdownView!
@IBOutlet private var blurView: UIVisualEffectView!

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -11,7 +11,7 @@
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="UpdateCell" id="Kqf-Pv-ca3" customClass="UpdateCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="UpdateCell" id="Kqf-Pv-ca3" customClass="UpdateCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="125"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
@@ -30,7 +30,7 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="uYl-PH-DuP">
<rect key="frame" x="0.0" y="0.0" width="343" height="125"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nop-pL-Icx" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nop-pL-Icx" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="343" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="88" id="EPP-7O-1Ad"/>
@@ -39,7 +39,7 @@
<stackView opaque="NO" contentMode="scaleToFill" alignment="firstBaseline" translatesAutoresizingMaskIntoConstraints="NO" id="RSR-5W-7tt" userLabel="Release Notes">
<rect key="frame" x="0.0" y="50" width="343" height="75"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Version Notes" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rNs-2O-k3V" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Version Notes" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rNs-2O-k3V" customClass="CollapsingMarkdownView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="15" y="0.0" width="313" height="26"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
@@ -91,7 +91,7 @@
<color red="1" green="1" blue="1" alpha="0.30000001192092896" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>