mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-17 18:53:40 +01:00
[CHANGE] Overhaul of the AppDetailView with version history, reviews & ratings, and app information
This commit is contained in:
@@ -25,12 +25,12 @@
|
|||||||
1F07F56B2955F11500F7BE95 /* AppScreenshotsPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */; };
|
1F07F56B2955F11500F7BE95 /* AppScreenshotsPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */; };
|
||||||
1F07F56F2955FB2000F7BE95 /* AppIDsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F07F56E2955FB2000F7BE95 /* AppIDsView.swift */; };
|
1F07F56F2955FB2000F7BE95 /* AppIDsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F07F56E2955FB2000F7BE95 /* AppIDsView.swift */; };
|
||||||
1F0DD81C2932D2FF007608A4 /* AppScreenshotsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */; };
|
1F0DD81C2932D2FF007608A4 /* AppScreenshotsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */; };
|
||||||
1F0DD81F2932D84C007608A4 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 1F0DD81E2932D84C007608A4 /* ExpandableText */; };
|
|
||||||
1F0DD8212933B749007608A4 /* AppPermissionsGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */; };
|
1F0DD8212933B749007608A4 /* AppPermissionsGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */; };
|
||||||
1F0DD83F29367F6C007608A4 /* ConnectAppleIDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD83E29367F6C007608A4 /* ConnectAppleIDView.swift */; };
|
1F0DD83F29367F6C007608A4 /* ConnectAppleIDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD83E29367F6C007608A4 /* ConnectAppleIDView.swift */; };
|
||||||
1F0DD84129368056007608A4 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD84029368056007608A4 /* EnvironmentValues.swift */; };
|
1F0DD84129368056007608A4 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD84029368056007608A4 /* EnvironmentValues.swift */; };
|
||||||
1F0DD8432936B0F9007608A4 /* RoundedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8422936B0F9007608A4 /* RoundedTextField.swift */; };
|
1F0DD8432936B0F9007608A4 /* RoundedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8422936B0F9007608A4 /* RoundedTextField.swift */; };
|
||||||
1F0DD8452936B3FE007608A4 /* FilledButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8442936B3FE007608A4 /* FilledButtonStyle.swift */; };
|
1F0DD8452936B3FE007608A4 /* FilledButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD8442936B3FE007608A4 /* FilledButtonStyle.swift */; };
|
||||||
|
1F1295812989B51F0048FCB9 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 1F1295802989B51F0048FCB9 /* ExpandableText */; };
|
||||||
1F2EF787297C4D40002FD839 /* LicensesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2EF786297C4D40002FD839 /* LicensesView.swift */; };
|
1F2EF787297C4D40002FD839 /* LicensesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2EF786297C4D40002FD839 /* LicensesView.swift */; };
|
||||||
1F44634529744E570070E514 /* HintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F44634429744E570070E514 /* HintView.swift */; };
|
1F44634529744E570070E514 /* HintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F44634429744E570070E514 /* HintView.swift */; };
|
||||||
1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5DF9D72974426300DDAA47 /* AppScreenshot.swift */; };
|
1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5DF9D72974426300DDAA47 /* AppScreenshot.swift */; };
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */; };
|
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */; };
|
||||||
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FEB292C171D007E68D1 /* NotificationManager.swift */; };
|
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FEB292C171D007E68D1 /* NotificationManager.swift */; };
|
||||||
1FB96FF3292D0539007E68D1 /* PillButtonProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */; };
|
1FB96FF3292D0539007E68D1 /* PillButtonProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */; };
|
||||||
|
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */; };
|
||||||
4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; };
|
4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; };
|
||||||
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
|
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
|
||||||
9922FFEC29B501C50020F868 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 9922FFEB29B501C50020F868 /* Starscream */; };
|
9922FFEC29B501C50020F868 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 9922FFEB29B501C50020F868 /* Starscream */; };
|
||||||
@@ -610,6 +611,7 @@
|
|||||||
1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriShortcutSetupView.swift; sourceTree = "<group>"; };
|
1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriShortcutSetupView.swift; sourceTree = "<group>"; };
|
||||||
1FB96FEB292C171D007E68D1 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
1FB96FEB292C171D007E68D1 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; };
|
1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; };
|
||||||
|
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHistoryView.swift; sourceTree = "<group>"; };
|
||||||
9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; };
|
9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftBridgeCore.swift; path = Dependencies/minimuxer/SwiftBridgeCore.swift; sourceTree = SOURCE_ROOT; };
|
99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftBridgeCore.swift; path = Dependencies/minimuxer/SwiftBridgeCore.swift; sourceTree = SOURCE_ROOT; };
|
||||||
99F87D1729D8E4C900B40039 /* minimuxer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = minimuxer.swift; path = Dependencies/minimuxer/minimuxer.swift; sourceTree = SOURCE_ROOT; };
|
99F87D1729D8E4C900B40039 /* minimuxer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = minimuxer.swift; path = Dependencies/minimuxer/minimuxer.swift; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -1019,8 +1021,8 @@
|
|||||||
B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */,
|
B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */,
|
||||||
191E5FB4290A5DA0001A3B7C /* libminimuxer.a in Frameworks */,
|
191E5FB4290A5DA0001A3B7C /* libminimuxer.a in Frameworks */,
|
||||||
19104DBC2909C4E500C49C7B /* libEmotionalDamage.a in Frameworks */,
|
19104DBC2909C4E500C49C7B /* libEmotionalDamage.a in Frameworks */,
|
||||||
|
1F1295812989B51F0048FCB9 /* ExpandableText in Frameworks */,
|
||||||
19104D952909BAEA00C49C7B /* libimobiledevice.a in Frameworks */,
|
19104D952909BAEA00C49C7B /* libimobiledevice.a in Frameworks */,
|
||||||
1F0DD81F2932D84C007608A4 /* ExpandableText in Frameworks */,
|
|
||||||
B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */,
|
B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */,
|
||||||
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
||||||
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */,
|
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */,
|
||||||
@@ -1156,6 +1158,7 @@
|
|||||||
1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */,
|
1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */,
|
||||||
1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */,
|
1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */,
|
||||||
1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */,
|
1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */,
|
||||||
|
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */,
|
||||||
);
|
);
|
||||||
path = "App Detail";
|
path = "App Detail";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2276,9 +2279,9 @@
|
|||||||
B3C395F8284F362400DA9E2F /* AppCenterCrashes */,
|
B3C395F8284F362400DA9E2F /* AppCenterCrashes */,
|
||||||
4879A9612861049C00FC1BBD /* OpenSSL */,
|
4879A9612861049C00FC1BBD /* OpenSSL */,
|
||||||
9922FFEB29B501C50020F868 /* Starscream */,
|
9922FFEB29B501C50020F868 /* Starscream */,
|
||||||
1F0DD81E2932D84C007608A4 /* ExpandableText */,
|
|
||||||
1F74FF1D295263510047C051 /* AsyncImage */,
|
1F74FF1D295263510047C051 /* AsyncImage */,
|
||||||
1F07F5662955D16A00F7BE95 /* SFSafeSymbols */,
|
1F07F5662955D16A00F7BE95 /* SFSafeSymbols */,
|
||||||
|
1F1295802989B51F0048FCB9 /* ExpandableText */,
|
||||||
);
|
);
|
||||||
productName = AltStore;
|
productName = AltStore;
|
||||||
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
||||||
@@ -2352,10 +2355,9 @@
|
|||||||
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */,
|
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */,
|
||||||
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
||||||
9922FFEA29B501C50020F868 /* XCRemoteSwiftPackageReference "Starscream" */,
|
9922FFEA29B501C50020F868 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||||
1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */,
|
|
||||||
1F0DD81D2932D84C007608A4 /* XCRemoteSwiftPackageReference "ExpandableText" */,
|
|
||||||
1F74FF1C295263510047C051 /* XCRemoteSwiftPackageReference "AsyncImage" */,
|
1F74FF1C295263510047C051 /* XCRemoteSwiftPackageReference "AsyncImage" */,
|
||||||
1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
||||||
|
1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */,
|
||||||
);
|
);
|
||||||
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -2780,6 +2782,7 @@
|
|||||||
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
||||||
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */,
|
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */,
|
||||||
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
||||||
|
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */,
|
||||||
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
||||||
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
||||||
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
||||||
@@ -3713,9 +3716,9 @@
|
|||||||
minimumVersion = 4.0.0;
|
minimumVersion = 4.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
1F0DD81D2932D84C007608A4 /* XCRemoteSwiftPackageReference "ExpandableText" */ = {
|
1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/NuPlay/ExpandableText";
|
repositoryURL = "https://github.com/fabianthdev/ExpandableText";
|
||||||
requirement = {
|
requirement = {
|
||||||
branch = main;
|
branch = main;
|
||||||
kind = branch;
|
kind = branch;
|
||||||
@@ -3832,9 +3835,9 @@
|
|||||||
package = 1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
package = 1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
||||||
productName = SFSafeSymbols;
|
productName = SFSafeSymbols;
|
||||||
};
|
};
|
||||||
1F0DD81E2932D84C007608A4 /* ExpandableText */ = {
|
1F1295802989B51F0048FCB9 /* ExpandableText */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 1F0DD81D2932D84C007608A4 /* XCRemoteSwiftPackageReference "ExpandableText" */;
|
package = 1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */;
|
||||||
productName = ExpandableText;
|
productName = ExpandableText;
|
||||||
};
|
};
|
||||||
1F74FF1D295263510047C051 /* AsyncImage */ = {
|
1F74FF1D295263510047C051 /* AsyncImage */ = {
|
||||||
|
|||||||
@@ -30,10 +30,10 @@
|
|||||||
{
|
{
|
||||||
"identity" : "expandabletext",
|
"identity" : "expandabletext",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/NuPlay/ExpandableText",
|
"location" : "https://github.com/fabianthdev/ExpandableText",
|
||||||
"state" : {
|
"state" : {
|
||||||
"branch" : "main",
|
"branch" : "main",
|
||||||
"revision" : "d140b404c6683bb169bb01ef4eeecdb6d9be8fb8"
|
"revision" : "a375f5b8c73f0af69aa7add890378fdf404a29bc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,16 +55,24 @@ internal enum L10n {
|
|||||||
internal static let restoreBackup = L10n.tr("Localizable", "AppAction.restoreBackup", fallback: "Restore backup")
|
internal static let restoreBackup = L10n.tr("Localizable", "AppAction.restoreBackup", fallback: "Restore backup")
|
||||||
}
|
}
|
||||||
internal enum AppDetailView {
|
internal enum AppDetailView {
|
||||||
|
///
|
||||||
|
internal static let information = L10n.tr("Localizable", "AppDetailView.information", fallback: "")
|
||||||
/// More...
|
/// More...
|
||||||
internal static let more = L10n.tr("Localizable", "AppDetailView.more", fallback: "More...")
|
internal static let more = L10n.tr("Localizable", "AppDetailView.more", fallback: "More...")
|
||||||
/// The app requires no permissions.
|
/// The app requires no permissions.
|
||||||
internal static let noPermissions = L10n.tr("Localizable", "AppDetailView.noPermissions", fallback: "The app requires no permissions.")
|
internal static let noPermissions = L10n.tr("Localizable", "AppDetailView.noPermissions", fallback: "The app requires no permissions.")
|
||||||
|
/// No screenshots available for this app.
|
||||||
|
internal static let noScreenshots = L10n.tr("Localizable", "AppDetailView.noScreenshots", fallback: "No screenshots available for this app.")
|
||||||
/// No version information
|
/// No version information
|
||||||
internal static let noVersionInformation = L10n.tr("Localizable", "AppDetailView.noVersionInformation", fallback: "No version information")
|
internal static let noVersionInformation = L10n.tr("Localizable", "AppDetailView.noVersionInformation", fallback: "No version information")
|
||||||
/// Permissions
|
/// Permissions
|
||||||
internal static let permissions = L10n.tr("Localizable", "AppDetailView.permissions", fallback: "Permissions")
|
internal static let permissions = L10n.tr("Localizable", "AppDetailView.permissions", fallback: "Permissions")
|
||||||
/// Version
|
/// Ratings & Reviews
|
||||||
internal static let version = L10n.tr("Localizable", "AppDetailView.version", fallback: "Version")
|
internal static let reviews = L10n.tr("Localizable", "AppDetailView.reviews", fallback: "Ratings & Reviews")
|
||||||
|
/// Version %@
|
||||||
|
internal static func version(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "AppDetailView.version", String(describing: p1), fallback: "Version %@")
|
||||||
|
}
|
||||||
/// What's New
|
/// What's New
|
||||||
internal static let whatsNew = L10n.tr("Localizable", "AppDetailView.whatsNew", fallback: "What's New")
|
internal static let whatsNew = L10n.tr("Localizable", "AppDetailView.whatsNew", fallback: "What's New")
|
||||||
internal enum Badge {
|
internal enum Badge {
|
||||||
@@ -73,6 +81,46 @@ internal enum L10n {
|
|||||||
/// From Trusted Source
|
/// From Trusted Source
|
||||||
internal static let trusted = L10n.tr("Localizable", "AppDetailView.Badge.trusted", fallback: "From Trusted Source")
|
internal static let trusted = L10n.tr("Localizable", "AppDetailView.Badge.trusted", fallback: "From Trusted Source")
|
||||||
}
|
}
|
||||||
|
internal enum Information {
|
||||||
|
/// Compatibility
|
||||||
|
internal static let compatibility = L10n.tr("Localizable", "AppDetailView.Information.compatibility", fallback: "Compatibility")
|
||||||
|
/// Requires iOS %@ or higher
|
||||||
|
internal static func compatibilityAtLeast(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "AppDetailView.Information.compatibilityAtLeast", String(describing: p1), fallback: "Requires iOS %@ or higher")
|
||||||
|
}
|
||||||
|
/// Requires iOS %@ or lower
|
||||||
|
internal static func compatibilityOrLower(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "AppDetailView.Information.compatibilityOrLower", String(describing: p1), fallback: "Requires iOS %@ or lower")
|
||||||
|
}
|
||||||
|
/// Unknown
|
||||||
|
internal static let compatibilityUnknown = L10n.tr("Localizable", "AppDetailView.Information.compatibilityUnknown", fallback: "Unknown")
|
||||||
|
/// Developer
|
||||||
|
internal static let developer = L10n.tr("Localizable", "AppDetailView.Information.developer", fallback: "Developer")
|
||||||
|
/// Latest Version
|
||||||
|
internal static let latestVersion = L10n.tr("Localizable", "AppDetailView.Information.latestVersion", fallback: "Latest Version")
|
||||||
|
/// Size
|
||||||
|
internal static let size = L10n.tr("Localizable", "AppDetailView.Information.size", fallback: "Size")
|
||||||
|
/// Source
|
||||||
|
internal static let source = L10n.tr("Localizable", "AppDetailView.Information.source", fallback: "Source")
|
||||||
|
}
|
||||||
|
internal enum Reviews {
|
||||||
|
/// out of %d
|
||||||
|
internal static func outOf(_ p1: Int) -> String {
|
||||||
|
return L10n.tr("Localizable", "AppDetailView.Reviews.outOf", p1, fallback: "out of %d")
|
||||||
|
}
|
||||||
|
/// %d Ratings
|
||||||
|
internal static func ratings(_ p1: Int) -> String {
|
||||||
|
return L10n.tr("Localizable", "AppDetailView.Reviews.ratings", p1, fallback: "%d Ratings")
|
||||||
|
}
|
||||||
|
/// See All
|
||||||
|
internal static let seeAll = L10n.tr("Localizable", "AppDetailView.Reviews.seeAll", fallback: "See All")
|
||||||
|
}
|
||||||
|
internal enum WhatsNew {
|
||||||
|
/// Show project on GitHub
|
||||||
|
internal static let showOnGithub = L10n.tr("Localizable", "AppDetailView.WhatsNew.showOnGithub", fallback: "Show project on GitHub")
|
||||||
|
/// Version History
|
||||||
|
internal static let versionHistory = L10n.tr("Localizable", "AppDetailView.WhatsNew.versionHistory", fallback: "Version History")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
internal enum AppIDsView {
|
internal enum AppIDsView {
|
||||||
/// Each app and app extension installed with SideStore must register an App ID with Apple.
|
/// Each app and app extension installed with SideStore must register an App ID with Apple.
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ struct DateFormatterHelper {
|
|||||||
dateComponentsFormatter.collapsesLargestUnit = false
|
dateComponentsFormatter.collapsesLargestUnit = false
|
||||||
return dateComponentsFormatter
|
return dateComponentsFormatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private static let relativeDateFormatter: RelativeDateTimeFormatter = {
|
||||||
|
let formatter = RelativeDateTimeFormatter()
|
||||||
|
formatter.unitsStyle = .abbreviated
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
private static let mediumDateFormatter: DateFormatter = {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateStyle = .medium
|
||||||
|
dateFormatter.timeStyle = .none
|
||||||
|
return dateFormatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static func string(forExpirationDate date: Date) -> String {
|
static func string(forExpirationDate date: Date) -> String {
|
||||||
@@ -32,10 +46,11 @@ struct DateFormatterHelper {
|
|||||||
self.appExpirationDateFormatter.unitsStyle = .full
|
self.appExpirationDateFormatter.unitsStyle = .full
|
||||||
self.appExpirationDateFormatter.allowedUnits = [.day]
|
self.appExpirationDateFormatter.allowedUnits = [.day]
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.appExpirationDateFormatter.string(from: startDate, to: date) ?? ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.appExpirationDateFormatter.string(from: startDate, to: date) ?? ""
|
return self.appExpirationDateFormatter.string(from: startDate, to: date) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func string(forRelativeDate date: Date, to referenceDate: Date = Date()) -> String {
|
||||||
|
self.relativeDateFormatter.localizedString(for: date, relativeTo: referenceDate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,12 +119,28 @@
|
|||||||
/* AppDetailView*/
|
/* AppDetailView*/
|
||||||
"AppDetailView.Badge.official" = "Official App";
|
"AppDetailView.Badge.official" = "Official App";
|
||||||
"AppDetailView.Badge.trusted" = "From Trusted Source";
|
"AppDetailView.Badge.trusted" = "From Trusted Source";
|
||||||
|
"AppDetailView.noScreenshots" = "No screenshots available for this app.";
|
||||||
"AppDetailView.more" = "More...";
|
"AppDetailView.more" = "More...";
|
||||||
"AppDetailView.whatsNew" = "What's New";
|
"AppDetailView.whatsNew" = "What's New";
|
||||||
"AppDetailView.version" = "Version";
|
"AppDetailView.WhatsNew.versionHistory" = "Version History";
|
||||||
|
"AppDetailView.WhatsNew.showOnGithub" = "Show project on GitHub";
|
||||||
|
"AppDetailView.reviews" = "Ratings & Reviews";
|
||||||
|
"AppDetailView.Reviews.outOf" = "out of %d";
|
||||||
|
"AppDetailView.Reviews.ratings" = "%d Ratings";
|
||||||
|
"AppDetailView.Reviews.seeAll" = "See All";
|
||||||
|
"AppDetailView.version" = "Version %@";
|
||||||
"AppDetailView.noVersionInformation" = "No version information";
|
"AppDetailView.noVersionInformation" = "No version information";
|
||||||
"AppDetailView.noPermissions" = "The app requires no permissions.";
|
"AppDetailView.noPermissions" = "The app requires no permissions.";
|
||||||
"AppDetailView.permissions" = "Permissions";
|
"AppDetailView.permissions" = "Permissions";
|
||||||
|
"AppDetailView.information" = "Information";
|
||||||
|
"AppDetailView.Information.source" = "Source";
|
||||||
|
"AppDetailView.Information.developer" = "Developer";
|
||||||
|
"AppDetailView.Information.size" = "Size";
|
||||||
|
"AppDetailView.Information.latestVersion" = "Latest Version";
|
||||||
|
"AppDetailView.Information.compatibility" = "Compatibility";
|
||||||
|
"AppDetailView.Information.compatibilityUnknown" = "Unknown";
|
||||||
|
"AppDetailView.Information.compatibilityAtLeast" = "Requires iOS %@ or higher";
|
||||||
|
"AppDetailView.Information.compatibilityOrLower" = "Requires iOS %@ or lower";
|
||||||
|
|
||||||
/* AppPermissionGrid */
|
/* AppPermissionGrid */
|
||||||
"AppPermissionGrid.usageDescription" = "Usage Description";
|
"AppPermissionGrid.usageDescription" = "Usage Description";
|
||||||
|
|||||||
@@ -16,14 +16,7 @@ struct AppDetailView: View {
|
|||||||
|
|
||||||
let storeApp: StoreApp
|
let storeApp: StoreApp
|
||||||
|
|
||||||
var dateFormatter: DateFormatter = {
|
let byteCountFormatter: ByteCountFormatter = {
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
dateFormatter.dateStyle = .medium
|
|
||||||
dateFormatter.timeStyle = .none
|
|
||||||
return dateFormatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
var byteCountFormatter: ByteCountFormatter = {
|
|
||||||
let formatter = ByteCountFormatter()
|
let formatter = ByteCountFormatter()
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
@@ -93,38 +86,81 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentView: some View {
|
var contentView: some View {
|
||||||
VStack(alignment: .leading, spacing: 32) {
|
VStack(alignment: .leading) {
|
||||||
if storeApp.sourceIdentifier == Source.altStoreIdentifier {
|
VStack(alignment: .leading, spacing: 32) {
|
||||||
officialAppBadge
|
if storeApp.sourceIdentifier == Source.altStoreIdentifier {
|
||||||
}
|
officialAppBadge
|
||||||
|
}
|
||||||
if let subtitle = storeApp.subtitle {
|
|
||||||
Text(subtitle)
|
if let subtitle = storeApp.subtitle {
|
||||||
.multilineTextAlignment(.center)
|
VStack {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
Image(systemSymbol: .quoteOpening)
|
||||||
|
.foregroundColor(.secondary.opacity(0.5))
|
||||||
|
.imageScale(.large)
|
||||||
|
.transformEffect(CGAffineTransform(a: 1, b: 0, c: -0.3, d: 1, tx: 0, ty: 0))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.offset(x: 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(subtitle)
|
||||||
|
.bold()
|
||||||
|
.italic()
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
Image(systemSymbol: .quoteClosing)
|
||||||
|
.foregroundColor(.secondary.opacity(0.5))
|
||||||
|
.imageScale(.large)
|
||||||
|
.transformEffect(CGAffineTransform(a: 1, b: 0, c: -0.3, d: 1, tx: 0, ty: 0))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
.offset(x: -30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !storeApp.screenshotURLs.isEmpty {
|
||||||
|
// Equatable: Only reload the view if the screenshots change.
|
||||||
|
// This prevents unnecessary redraws on scroll.
|
||||||
|
AppScreenshotsScrollView(urls: storeApp.screenshotURLs)
|
||||||
|
.equatable()
|
||||||
|
} else {
|
||||||
|
VStack() {
|
||||||
|
Text(L10n.AppDetailView.noScreenshots)
|
||||||
|
.italic()
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandableText(text: storeApp.localizedDescription)
|
||||||
|
.lineLimit(6)
|
||||||
|
.expandButton(TextSet(text: L10n.AppDetailView.more, font: .callout, color: .accentColor))
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !storeApp.screenshotURLs.isEmpty {
|
|
||||||
// Equatable: Only reload the view if the screenshots change.
|
VStack(spacing: 16) {
|
||||||
// This prevents unnecessary redraws on scroll.
|
Divider()
|
||||||
AppScreenshotsScrollView(urls: storeApp.screenshotURLs)
|
|
||||||
.equatable()
|
currentVersionView
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
ratingsView
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
permissionsView
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
informationView
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
ExpandableText(text: storeApp.localizedDescription)
|
|
||||||
.lineLimit(6)
|
|
||||||
.expandButton(TextSet(text: L10n.AppDetailView.more, font: .callout, color: .accentColor))
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
currentVersionView
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
permissionsView
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
// TODO: Add review view
|
|
||||||
// Only let users rate the app if it is currently installed!
|
|
||||||
}
|
}
|
||||||
.padding(.vertical)
|
.padding(.vertical)
|
||||||
.background(
|
.background(
|
||||||
@@ -156,25 +192,26 @@ struct AppDetailView: View {
|
|||||||
|
|
||||||
var currentVersionView: some View {
|
var currentVersionView: some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack(alignment: .bottom) {
|
VStack {
|
||||||
VStack(alignment: .leading) {
|
HStack(alignment: .firstTextBaseline) {
|
||||||
Text(L10n.AppDetailView.whatsNew)
|
Text(L10n.AppDetailView.whatsNew)
|
||||||
.bold()
|
.bold()
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
|
|
||||||
if let version = storeApp.latestVersion?.version {
|
Spacer()
|
||||||
Text("\(L10n.AppDetailView.version) \(version)")
|
|
||||||
.font(.callout)
|
NavigationLink {
|
||||||
.foregroundColor(.secondary)
|
AppVersionHistoryView(storeApp: self.storeApp)
|
||||||
|
} label: {
|
||||||
|
Text(L10n.AppDetailView.WhatsNew.versionHistory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if let versionDate = storeApp.versionDate, let versionSize = storeApp.size {
|
if let latestVersion = storeApp.latestVersion {
|
||||||
VStack(alignment: .trailing) {
|
HStack {
|
||||||
Text(dateFormatter.string(from: versionDate))
|
Text(L10n.AppDetailView.version(latestVersion.version))
|
||||||
Text(byteCountFormatter.string(fromByteCount: Int64(versionSize)))
|
Spacer()
|
||||||
|
Text(DateFormatterHelper.string(forRelativeDate: latestVersion.date))
|
||||||
}
|
}
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@@ -189,6 +226,109 @@ struct AppDetailView: View {
|
|||||||
Text(L10n.AppDetailView.noVersionInformation)
|
Text(L10n.AppDetailView.noVersionInformation)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if true {
|
||||||
|
SwiftUI.Button {
|
||||||
|
UIApplication.shared.open(URL(string: "https://github.com/SideStore/SideStore")!) { _ in }
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(L10n.AppDetailView.WhatsNew.showOnGithub)
|
||||||
|
Image(systemSymbol: .arrowUpForwardSquare)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ratingsView: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack(alignment: .firstTextBaseline) {
|
||||||
|
Text(L10n.AppDetailView.whatsNew)
|
||||||
|
.bold()
|
||||||
|
.font(.title3)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
NavigationLink {
|
||||||
|
AppVersionHistoryView(storeApp: self.storeApp)
|
||||||
|
} label: {
|
||||||
|
Text(L10n.AppDetailView.Reviews.seeAll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack(spacing: 40) {
|
||||||
|
VStack {
|
||||||
|
Text("3.0")
|
||||||
|
.font(.system(size: 48, weight: .bold, design: .rounded))
|
||||||
|
.opacity(0.8)
|
||||||
|
Text(L10n.AppDetailView.Reviews.outOf(5))
|
||||||
|
.bold()
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .trailing) {
|
||||||
|
LazyVGrid(columns: [GridItem(.fixed(48), alignment: .trailing), GridItem(.flexible())], spacing: 2) {
|
||||||
|
ForEach(Array(1...5).reversed(), id: \.self) { rating in
|
||||||
|
HStack(spacing: 2) {
|
||||||
|
ForEach(0..<rating) { _ in
|
||||||
|
Image(systemSymbol: .starFill)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressView(value: 0.5)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.progressViewStyle(LinearProgressViewStyle(tint: .secondary))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
Text(L10n.AppDetailView.Reviews.ratings(5))
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TabView {
|
||||||
|
ForEach(0..<5) { i in
|
||||||
|
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
HStack {
|
||||||
|
Text("Review \(i + 1)")
|
||||||
|
.bold()
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text(DateFormatterHelper.string(forRelativeDate: Date().addingTimeInterval(-60*60)))
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
RatingStars(rating: i + 1)
|
||||||
|
.frame(height: 12)
|
||||||
|
.foregroundColor(.yellow)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandableText(text: "Long review text content here.\nMultiple lines.\nAt least three are shown.\nBut are there more?")
|
||||||
|
.lineLimit(3)
|
||||||
|
.expandButton(TextSet(text: L10n.AppDetailView.more, font: .callout, color: .accentColor))
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
}
|
||||||
|
.tag(i)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
|
.frame(height: 150)
|
||||||
|
.padding(.horizontal, -16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,4 +349,53 @@ struct AppDetailView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var informationData: [(title: String, content: String)] {
|
||||||
|
var data: [(title: String, content: String)] = [
|
||||||
|
(L10n.AppDetailView.Information.source, self.storeApp.source?.name ?? ""),
|
||||||
|
(L10n.AppDetailView.Information.developer, self.storeApp.developerName),
|
||||||
|
// ("Category", self.storeApp.category),
|
||||||
|
]
|
||||||
|
|
||||||
|
if let latestVersion = self.storeApp.latestVersion {
|
||||||
|
data += [
|
||||||
|
(L10n.AppDetailView.Information.size, self.byteCountFormatter.string(fromByteCount: latestVersion.size)),
|
||||||
|
(L10n.AppDetailView.Information.latestVersion, self.storeApp.latestVersion?.version ?? ""),
|
||||||
|
]
|
||||||
|
|
||||||
|
var compatibility: String = L10n.AppDetailView.Information.compatibilityUnknown
|
||||||
|
let iOSVersion = ProcessInfo.processInfo.operatingSystemVersion
|
||||||
|
|
||||||
|
if let minOSVersion = latestVersion.minOSVersion, ProcessInfo.processInfo.isOperatingSystemAtLeast(minOSVersion) == false {
|
||||||
|
compatibility = L10n.AppDetailView.Information.compatibilityAtLeast(minOSVersion.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let maxOSVersion = latestVersion.maxOSVersion,
|
||||||
|
(!ProcessInfo.processInfo.isOperatingSystemAtLeast(maxOSVersion) || maxOSVersion.stringValue.compare(iOSVersion.stringValue, options: .numeric) == .orderedSame) {
|
||||||
|
compatibility = L10n.AppDetailView.Information.compatibilityOrLower(maxOSVersion.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.append((L10n.AppDetailView.Information.compatibility, compatibility))
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
var informationView: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(L10n.AppDetailView.information)
|
||||||
|
.bold()
|
||||||
|
.font(.title3)
|
||||||
|
|
||||||
|
LazyVGrid(columns: [GridItem(.flexible(), alignment: .leading), GridItem(.flexible(), alignment: .trailing)], spacing: 8) {
|
||||||
|
ForEach(informationData, id: \.title) { title, content in
|
||||||
|
Text(title)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
|
||||||
|
Text(content)
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ struct AppScreenshotsPreview: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
|
.navigationTitle("\(index + 1) of \(self.urls.count)")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
SwiftUI.Button {
|
SwiftUI.Button {
|
||||||
|
|||||||
49
AltStore/Views/App Detail/AppVersionHistoryView.swift
Normal file
49
AltStore/Views/App Detail/AppVersionHistoryView.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// AppVersionHistoryView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 28.01.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import AltStoreCore
|
||||||
|
import ExpandableText
|
||||||
|
|
||||||
|
struct AppVersionHistoryView: View {
|
||||||
|
let storeApp: StoreApp
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
ForEach(storeApp.versions.sorted(by: { $0.date < $1.date }), id: \.version) { version in
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
HStack {
|
||||||
|
Text(version.version).bold()
|
||||||
|
Spacer()
|
||||||
|
Text(DateFormatterHelper.string(forRelativeDate: version.date))
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let versionDescription = version.localizedDescription {
|
||||||
|
ExpandableText(text: versionDescription)
|
||||||
|
.lineLimit(3)
|
||||||
|
.expandButton(TextSet(text: L10n.AppDetailView.more, font: .callout, color: .accentColor))
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
} else {
|
||||||
|
Text("No version desciption available")
|
||||||
|
.italic()
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listStyle(PlainListStyle())
|
||||||
|
.navigationTitle("Version History")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//struct AppVersionHistoryView_Previews: PreviewProvider {
|
||||||
|
// static var previews: some View {
|
||||||
|
// AppVersionHistoryView(storeApp: )
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -50,13 +50,15 @@ struct NewsItemView: View {
|
|||||||
.bold()
|
.bold()
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
HStack(spacing: 0) {
|
||||||
if let sourceName = newsItem.source?.name {
|
if let sourceName = newsItem.source?.name {
|
||||||
Text(sourceName)
|
Text(sourceName)
|
||||||
.italic()
|
.italic()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let externalURL = newsItem.externalURL {
|
if let externalURL = newsItem.externalURL {
|
||||||
|
Text(" • ")
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Image(systemSymbol: .link)
|
Image(systemSymbol: .link)
|
||||||
Text(externalURL.host ?? "")
|
Text(externalURL.host ?? "")
|
||||||
|
|||||||
@@ -61,8 +61,10 @@ struct RootView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color(UIColor.altPrimary))
|
.foregroundColor(.white)
|
||||||
|
.background(Color.accentColor)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
|
.shadow(radius: 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -83,7 +85,7 @@ extension RootView {
|
|||||||
switch self {
|
switch self {
|
||||||
case .news: return .newspaper
|
case .news: return .newspaper
|
||||||
case .browse: return .booksVertical
|
case .browse: return .booksVertical
|
||||||
case .myApps: return .appBadge
|
case .myApps: return .squareStack
|
||||||
case .settings: return .gearshape
|
case .settings: return .gearshape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user