mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-21 12:43:25 +01:00
Displays remaining App ID count
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" 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="15705" 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"/>
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
@@ -629,7 +629,7 @@ World</string>
|
|||||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="10" id="SB5-U0-jyy">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="10" id="SB5-U0-jyy">
|
||||||
<size key="itemSize" width="375" height="60"/>
|
<size key="itemSize" width="375" height="60"/>
|
||||||
<size key="headerReferenceSize" width="50" height="50"/>
|
<size key="headerReferenceSize" width="50" height="50"/>
|
||||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
<size key="footerReferenceSize" width="50" height="50"/>
|
||||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
</collectionViewFlowLayout>
|
</collectionViewFlowLayout>
|
||||||
<cells>
|
<cells>
|
||||||
@@ -750,6 +750,39 @@ World</string>
|
|||||||
<outlet property="textLabel" destination="BDU-hM-rro" id="CQM-8K-bcH"/>
|
<outlet property="textLabel" destination="BDU-hM-rro" id="CQM-8K-bcH"/>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionReusableView>
|
</collectionReusableView>
|
||||||
|
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="InstalledAppsFooter" id="HYs-co-nJZ" customClass="InstalledAppsCollectionFooterView" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="185" width="375" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="5/10 App IDs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LLv-8I-6Of">
|
||||||
|
<rect key="frame" x="138.5" y="0.0" width="98" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NHb-0F-cHZ">
|
||||||
|
<rect key="frame" x="241.5" y="-0.5" width="22" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="22" id="K1V-bV-Mjg"/>
|
||||||
|
<constraint firstAttribute="height" constant="22" id="vW1-aN-slG"/>
|
||||||
|
</constraints>
|
||||||
|
<state key="normal" image="questionmark.circle" catalog="system"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="presentAppIDHelpAlert:" destination="hv7-Ar-voT" eventType="touchUpInside" id="m2N-Lx-adY"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="NHb-0F-cHZ" firstAttribute="leading" secondItem="LLv-8I-6Of" secondAttribute="trailing" constant="5" id="Fpk-zD-6AK"/>
|
||||||
|
<constraint firstItem="LLv-8I-6Of" firstAttribute="top" secondItem="HYs-co-nJZ" secondAttribute="top" id="UVG-MJ-SPb"/>
|
||||||
|
<constraint firstItem="LLv-8I-6Of" firstAttribute="centerX" secondItem="HYs-co-nJZ" secondAttribute="centerX" id="Wc0-ul-McA"/>
|
||||||
|
<constraint firstItem="NHb-0F-cHZ" firstAttribute="centerY" secondItem="LLv-8I-6Of" secondAttribute="centerY" id="wSf-Lv-NC0"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="button" destination="NHb-0F-cHZ" id="wOh-Ee-jhN"/>
|
||||||
|
<outlet property="textLabel" destination="LLv-8I-6Of" id="t2D-f1-5pC"/>
|
||||||
|
</connections>
|
||||||
|
</collectionReusableView>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="dataSource" destination="hv7-Ar-voT" id="YOx-f4-chF"/>
|
<outlet property="dataSource" destination="hv7-Ar-voT" id="YOx-f4-chF"/>
|
||||||
<outlet property="delegate" destination="hv7-Ar-voT" id="1PN-pf-cZK"/>
|
<outlet property="delegate" destination="hv7-Ar-voT" id="1PN-pf-cZK"/>
|
||||||
@@ -797,6 +830,7 @@ World</string>
|
|||||||
<image name="MyApps" width="20" height="20"/>
|
<image name="MyApps" width="20" height="20"/>
|
||||||
<image name="News" width="19" height="20"/>
|
<image name="News" width="19" height="20"/>
|
||||||
<image name="Settings" width="20" height="20"/>
|
<image name="Settings" width="20" height="20"/>
|
||||||
|
<image name="questionmark.circle" catalog="system" width="64" height="60"/>
|
||||||
<namedColor name="Background">
|
<namedColor name="Background">
|
||||||
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ class InstalledAppsCollectionHeaderView: UICollectionReusableView
|
|||||||
@IBOutlet var button: UIButton!
|
@IBOutlet var button: UIButton!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InstalledAppsCollectionFooterView: UICollectionReusableView
|
||||||
|
{
|
||||||
|
@IBOutlet var textLabel: UILabel!
|
||||||
|
@IBOutlet var button: UIButton!
|
||||||
|
}
|
||||||
|
|
||||||
class NoUpdatesCollectionViewCell: UICollectionViewCell
|
class NoUpdatesCollectionViewCell: UICollectionViewCell
|
||||||
{
|
{
|
||||||
@IBOutlet var blurView: UIVisualEffectView!
|
@IBOutlet var blurView: UIVisualEffectView!
|
||||||
|
|||||||
@@ -615,7 +615,7 @@ private extension MyAppsViewController
|
|||||||
|
|
||||||
func presentSideloadingAlert(completion: @escaping (Bool) -> Void)
|
func presentSideloadingAlert(completion: @escaping (Bool) -> Void)
|
||||||
{
|
{
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Sideload Apps (Beta)", comment: ""), message: NSLocalizedString("You may only install 10 apps + app extensions per week due to Apple's restrictions.\n\nIf you encounter an app that is not able to be sideloaded, please report the app to support@altstore.io.", comment: ""), preferredStyle: .alert)
|
let alertController = UIAlertController(title: NSLocalizedString("Sideload Apps (Beta)", comment: ""), message: NSLocalizedString("If you encounter an app that is not able to be sideloaded, please report the app to support@altstore.io.", comment: ""), preferredStyle: .alert)
|
||||||
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .default, handler: { (action) in
|
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .default, handler: { (action) in
|
||||||
completion(true)
|
completion(true)
|
||||||
}))
|
}))
|
||||||
@@ -700,6 +700,19 @@ private extension MyAppsViewController
|
|||||||
|
|
||||||
self.present(alertController, animated: true, completion: nil)
|
self.present(alertController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func presentAppIDHelpAlert(_ sender: UIButton)
|
||||||
|
{
|
||||||
|
let message = NSLocalizedString("""
|
||||||
|
Each app and app extension installed with AltStore must register an App ID with Apple. Apple limits free developer accounts to 10 App IDs at a time.
|
||||||
|
|
||||||
|
App IDs expire after one week, but AltStore will automatically renew them for all installed apps. Once an App ID expires, it no longer counts toward your total.
|
||||||
|
""", comment: "")
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: NSLocalizedString("What are App IDs?", comment: ""), message: message, preferredStyle: .alert)
|
||||||
|
alertController.addAction(.ok)
|
||||||
|
self.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MyAppsViewController
|
private extension MyAppsViewController
|
||||||
@@ -805,7 +818,7 @@ extension MyAppsViewController
|
|||||||
|
|
||||||
return headerView
|
return headerView
|
||||||
|
|
||||||
case .installedApps:
|
case .installedApps where kind == UICollectionView.elementKindSectionHeader:
|
||||||
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InstalledAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
|
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InstalledAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
UIView.performWithoutAnimation {
|
||||||
@@ -821,6 +834,29 @@ extension MyAppsViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return headerView
|
return headerView
|
||||||
|
|
||||||
|
case .installedApps:
|
||||||
|
let installedApps = self.installedAppsDataSource.fetchedResultsController.fetchedObjects ?? []
|
||||||
|
let registeredAppIDs = installedApps.filter { $0.team?.isActiveTeam ?? false }.reduce(0) { (sum, installedApp) in
|
||||||
|
// Each InstallApp has it's own app ID, plus one for each app extension.
|
||||||
|
return sum + 1 + installedApp.appExtensions.count
|
||||||
|
}
|
||||||
|
|
||||||
|
let maximumAppIDCount = 10
|
||||||
|
let remainingAppIDs = max(maximumAppIDCount - registeredAppIDs, 0)
|
||||||
|
|
||||||
|
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "InstalledAppsFooter", for: indexPath) as! InstalledAppsCollectionFooterView
|
||||||
|
|
||||||
|
if remainingAppIDs == 1
|
||||||
|
{
|
||||||
|
footerView.textLabel.text = String(format: NSLocalizedString("1 App ID Remaining", comment: ""))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
footerView.textLabel.text = String(format: NSLocalizedString("%@ App IDs Remaining", comment: ""), NSNumber(value: remainingAppIDs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return footerView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,6 +925,29 @@ extension MyAppsViewController: UICollectionViewDelegateFlowLayout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize
|
||||||
|
{
|
||||||
|
let section = Section.allCases[section]
|
||||||
|
switch section
|
||||||
|
{
|
||||||
|
case .noUpdates: return .zero
|
||||||
|
case .updates: return .zero
|
||||||
|
case .installedApps:
|
||||||
|
#if BETA
|
||||||
|
if let team = DatabaseManager.shared.activeTeam(), team.type == .free
|
||||||
|
{
|
||||||
|
return CGSize(width: collectionView.bounds.width, height: 44)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return .zero
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return .zero
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func collectionView(_ myCV: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
|
func collectionView(_ myCV: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
|
||||||
{
|
{
|
||||||
let section = Section.allCases[section]
|
let section = Section.allCases[section]
|
||||||
|
|||||||
@@ -164,6 +164,12 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
|
|||||||
team.isActiveTeam = false
|
team.isActiveTeam = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let altStoreApp = InstalledApp.fetchAltStore(in: context), altStoreApp.team == nil
|
||||||
|
{
|
||||||
|
// No team assigned to AltStore app yet, so assume this team was used to originally install it.
|
||||||
|
altStoreApp.team = team
|
||||||
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
try context.save()
|
try context.save()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user