mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-18 19:23:43 +01:00
Merge branch 'fabianthdev/feature/SwiftUI' into naturecodevoid/swiftui-improvements
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
1F1295812989B51F0048FCB9 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 1F1295802989B51F0048FCB9 /* ExpandableText */; };
|
1F1295812989B51F0048FCB9 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 1F1295802989B51F0048FCB9 /* ExpandableText */; };
|
||||||
1F180F92298E7A1B00D1C98B /* StoreApp+Trusted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */; };
|
1F180F92298E7A1B00D1C98B /* StoreApp+Trusted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */; };
|
||||||
1F180F94298E7A2500D1C98B /* Source+Trusted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F180F93298E7A2500D1C98B /* Source+Trusted.swift */; };
|
1F180F94298E7A2500D1C98B /* Source+Trusted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F180F93298E7A2500D1C98B /* Source+Trusted.swift */; };
|
||||||
|
1F1D669E29A234CE0095BFCD /* WriteAppReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1D669D29A234CE0095BFCD /* WriteAppReviewView.swift */; };
|
||||||
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 */; };
|
||||||
1F545E83298D79E400589F68 /* ErrorLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F545E82298D79E400589F68 /* ErrorLogView.swift */; };
|
1F545E83298D79E400589F68 /* ErrorLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F545E82298D79E400589F68 /* ErrorLogView.swift */; };
|
||||||
@@ -594,6 +595,7 @@
|
|||||||
1F0DD8442936B3FE007608A4 /* FilledButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilledButtonStyle.swift; sourceTree = "<group>"; };
|
1F0DD8442936B3FE007608A4 /* FilledButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilledButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StoreApp+Trusted.swift"; sourceTree = "<group>"; };
|
1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StoreApp+Trusted.swift"; sourceTree = "<group>"; };
|
||||||
1F180F93298E7A2500D1C98B /* Source+Trusted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Source+Trusted.swift"; sourceTree = "<group>"; };
|
1F180F93298E7A2500D1C98B /* Source+Trusted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Source+Trusted.swift"; sourceTree = "<group>"; };
|
||||||
|
1F1D669D29A234CE0095BFCD /* WriteAppReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteAppReviewView.swift; sourceTree = "<group>"; };
|
||||||
1F2EF786297C4D40002FD839 /* LicensesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesView.swift; sourceTree = "<group>"; };
|
1F2EF786297C4D40002FD839 /* LicensesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesView.swift; sourceTree = "<group>"; };
|
||||||
1F44634429744E570070E514 /* HintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintView.swift; sourceTree = "<group>"; };
|
1F44634429744E570070E514 /* HintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintView.swift; sourceTree = "<group>"; };
|
||||||
1F545E82298D79E400589F68 /* ErrorLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogView.swift; sourceTree = "<group>"; };
|
1F545E82298D79E400589F68 /* ErrorLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogView.swift; sourceTree = "<group>"; };
|
||||||
@@ -1193,6 +1195,7 @@
|
|||||||
1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */,
|
1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */,
|
||||||
1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */,
|
1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */,
|
||||||
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */,
|
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */,
|
||||||
|
1F1D669D29A234CE0095BFCD /* WriteAppReviewView.swift */,
|
||||||
);
|
);
|
||||||
path = "App Detail";
|
path = "App Detail";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2889,6 +2892,7 @@
|
|||||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
||||||
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
|
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
|
||||||
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
|
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
|
||||||
|
1F1D669E29A234CE0095BFCD /* WriteAppReviewView.swift in Sources */,
|
||||||
1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */,
|
1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */,
|
||||||
99DE640329A1624500B920BF /* View+SideStore.swift in Sources */,
|
99DE640329A1624500B920BF /* View+SideStore.swift in Sources */,
|
||||||
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */,
|
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */,
|
||||||
|
|||||||
@@ -55,3 +55,26 @@ extension AppIconView: Equatable {
|
|||||||
lhs.iconUrl == rhs.iconUrl && lhs.cornerRadius == rhs.cornerRadius
|
lhs.iconUrl == rhs.iconUrl && lhs.cornerRadius == rhs.cornerRadius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import AltStoreCore
|
||||||
|
|
||||||
|
struct AppIconView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
HStack {
|
||||||
|
AppIconView(iconUrl: app.iconURL)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(app.name)
|
||||||
|
.bold()
|
||||||
|
Text(app.developerName)
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ struct AppPillButton: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func refreshApp(_ installedApp: InstalledApp) {
|
func refreshApp(_ installedApp: InstalledApp) {
|
||||||
|
AppManager.shared.refresh([installedApp], presentingViewController: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func installApp(_ storeApp: StoreApp) {
|
func installApp(_ storeApp: StoreApp) {
|
||||||
@@ -100,8 +100,45 @@ struct AppPillButton: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct AppPillButton_Previews: PreviewProvider {
|
struct AppPillButton_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
|
||||||
// AppPillButton()
|
static let context = DatabaseManager.shared.viewContext
|
||||||
// }
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
//}
|
static let installedApp = InstalledApp.fetchAltStore(in: context)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
VStack {
|
||||||
|
self.preview(for: app)
|
||||||
|
|
||||||
|
self.preview(for: installedApp!)
|
||||||
|
|
||||||
|
self.preview(for: installedApp!, showRemainingDays: true)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
static func preview(for app: AppProtocol, showRemainingDays: Bool = false) -> some View {
|
||||||
|
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
|
||||||
|
HStack {
|
||||||
|
AppIconView(iconUrl: self.app.iconURL)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(app is StoreApp ? "Store App" : "Installed App")
|
||||||
|
.bold()
|
||||||
|
Text(
|
||||||
|
app is StoreApp ?
|
||||||
|
"Can be installed" :
|
||||||
|
showRemainingDays ? "Can be refreshed" : "Can be opened"
|
||||||
|
)
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
AppPillButton(app: app, showRemainingDays: showRemainingDays)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,9 +41,15 @@ extension AppScreenshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import AltStoreCore
|
||||||
|
|
||||||
struct AppScreenshot_Previews: PreviewProvider {
|
struct AppScreenshot_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
AppScreenshot(url: URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/browse-dark.png")!)
|
AppScreenshot(url: app.screenshotURLs[0])
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ struct ModalNavigationLink<Label: View, Modal: View>: View {
|
|||||||
|
|
||||||
@State var isPresentingModal: Bool = false
|
@State var isPresentingModal: Bool = false
|
||||||
|
|
||||||
|
init(@ViewBuilder modal: @escaping () -> Modal, @ViewBuilder label: @escaping () -> Label) {
|
||||||
|
self.modal = modal
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ title: String, @ViewBuilder modal: @escaping () -> Modal) where Label == Text {
|
||||||
|
self.modal = modal
|
||||||
|
self.label = { Text(title) }
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
SwiftUI.Button {
|
SwiftUI.Button {
|
||||||
self.isPresentingModal = true
|
self.isPresentingModal = true
|
||||||
@@ -26,8 +36,10 @@ struct ModalNavigationLink<Label: View, Modal: View>: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct ModalNavigationLink_Previews: PreviewProvider {
|
struct ModalNavigationLink_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
static var previews: some View {
|
||||||
// ModalNavigationLink()
|
ModalNavigationLink("Present Modal") {
|
||||||
// }
|
Text("Modal")
|
||||||
//}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import SwiftUI
|
|||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
|
||||||
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
|
@ViewBuilder func `if`<Content: View>(_ condition: Bool, @ViewBuilder transform: (Self) -> Content) -> some View {
|
||||||
if condition {
|
if condition {
|
||||||
transform(self)
|
transform(self)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ struct AppDetailView: View {
|
|||||||
let maxContentCornerRadius: CGFloat = 24
|
let maxContentCornerRadius: CGFloat = 24
|
||||||
let headerViewHeight: CGFloat = 140
|
let headerViewHeight: CGFloat = 140
|
||||||
let permissionColumns = 4
|
let permissionColumns = 4
|
||||||
|
|
||||||
var headerBlurRadius: CGFloat {
|
var headerBlurRadius: CGFloat {
|
||||||
min(20, max(0, 20 - (scrollOffset / -150) * 20))
|
min(20, max(0, 20 - (scrollOffset / -150) * 20))
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,11 @@ struct AppDetailView: View {
|
|||||||
var contentCornerRadius: CGFloat {
|
var contentCornerRadius: CGFloat {
|
||||||
max(CGFloat.zero, min(maxContentCornerRadius, maxContentCornerRadius * (1 - self.scrollOffset / self.headerViewHeight)))
|
max(CGFloat.zero, min(maxContentCornerRadius, maxContentCornerRadius * (1 - self.scrollOffset / self.headerViewHeight)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var canRateApp: Bool {
|
||||||
|
self.storeApp.installedApp != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ObservableScrollView(scrollOffset: $scrollOffset) { proxy in
|
ObservableScrollView(scrollOffset: $scrollOffset) { proxy in
|
||||||
@@ -86,7 +91,7 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentView: some View {
|
var contentView: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading, spacing: 24) {
|
||||||
VStack(alignment: .leading, spacing: 32) {
|
VStack(alignment: .leading, spacing: 32) {
|
||||||
if storeApp.isFromOfficialSource {
|
if storeApp.isFromOfficialSource {
|
||||||
officialAppBadge
|
officialAppBadge
|
||||||
@@ -145,7 +150,7 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 24) {
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
currentVersionView
|
currentVersionView
|
||||||
@@ -179,23 +184,29 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var officialAppBadge: some View {
|
var officialAppBadge: some View {
|
||||||
HStack {
|
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
|
||||||
Spacer()
|
HStack {
|
||||||
Image(systemSymbol: .checkmarkSealFill)
|
Spacer()
|
||||||
Text(L10n.AppDetailView.Badge.official)
|
Image(systemSymbol: .checkmarkSealFill)
|
||||||
Spacer()
|
Text(L10n.AppDetailView.Badge.official)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
.foregroundColor(.accentColor)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
var trustedAppBadge: some View {
|
var trustedAppBadge: some View {
|
||||||
HStack {
|
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
|
||||||
Spacer()
|
HStack {
|
||||||
Image(systemSymbol: .shieldLefthalfFill)
|
Spacer()
|
||||||
Text(L10n.AppDetailView.Badge.trusted)
|
Image(systemSymbol: .shieldLefthalfFill)
|
||||||
Spacer()
|
Text(L10n.AppDetailView.Badge.trusted)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
.foregroundColor(.accentColor)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentVersionView: some View {
|
var currentVersionView: some View {
|
||||||
@@ -318,7 +329,7 @@ struct AppDetailView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
RatingStars(rating: i + 1)
|
RatingStars(rating: 5 - i)
|
||||||
.frame(height: 12)
|
.frame(height: 12)
|
||||||
.foregroundColor(.yellow)
|
.foregroundColor(.yellow)
|
||||||
}
|
}
|
||||||
@@ -337,6 +348,17 @@ struct AppDetailView: View {
|
|||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
.frame(height: 150)
|
.frame(height: 150)
|
||||||
.padding(.horizontal, -16)
|
.padding(.horizontal, -16)
|
||||||
|
|
||||||
|
if self.canRateApp {
|
||||||
|
ModalNavigationLink {
|
||||||
|
NavigationView {
|
||||||
|
WriteAppReviewView(storeApp: self.storeApp)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Write a Review", systemSymbol: .squareAndPencil)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,9 +375,8 @@ struct AppDetailView: View {
|
|||||||
} else {
|
} else {
|
||||||
AppPermissionsGrid(permissions: storeApp.permissions)
|
AppPermissionsGrid(permissions: storeApp.permissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
var informationData: [(title: String, content: String)] {
|
var informationData: [(title: String, content: String)] {
|
||||||
@@ -418,3 +439,16 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct AppDetailView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
AppDetailView(storeApp: app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,8 +55,17 @@ extension AppScreenshotsPreview: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct AppScreenshotsPreview_Previews: PreviewProvider {
|
struct AppScreenshotsPreview_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
|
||||||
// AppScreenshotsPreview()
|
static let context = DatabaseManager.shared.viewContext
|
||||||
// }
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
//}
|
|
||||||
|
static var previews: some View {
|
||||||
|
Color.clear
|
||||||
|
.sheet(isPresented: .constant(true)) {
|
||||||
|
NavigationView {
|
||||||
|
AppScreenshotsPreview(urls: app.screenshotURLs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,3 +56,16 @@ extension Int: Identifiable {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import AltStoreCore
|
||||||
|
|
||||||
|
struct AppScreenshotsScrollView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
AppScreenshotsScrollView(urls: app.screenshotURLs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,8 +42,14 @@ struct AppVersionHistoryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct AppVersionHistoryView_Previews: PreviewProvider {
|
struct AppVersionHistoryView_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
|
||||||
// AppVersionHistoryView(storeApp: )
|
static let context = DatabaseManager.shared.viewContext
|
||||||
// }
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
//}
|
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
AppVersionHistoryView(storeApp: app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
102
AltStore/Views/App Detail/WriteAppReviewView.swift
Normal file
102
AltStore/Views/App Detail/WriteAppReviewView.swift
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// WriteAppReviewView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 19.02.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import AltStoreCore
|
||||||
|
|
||||||
|
struct WriteAppReviewView: View {
|
||||||
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
|
let storeApp: StoreApp
|
||||||
|
|
||||||
|
@State var currentRating = 0
|
||||||
|
@State var reviewText = ""
|
||||||
|
|
||||||
|
var canSendReview: Bool {
|
||||||
|
// Only allow the user to send the review if a rating has been set and
|
||||||
|
// the review text is either empty or doesn't contain only whitespaces.
|
||||||
|
self.currentRating > 0 && (
|
||||||
|
self.reviewText.isEmpty || !self.reviewText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
// App Information
|
||||||
|
HStack {
|
||||||
|
AppIconView(iconUrl: storeApp.iconURL, size: 50)
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(storeApp.name)
|
||||||
|
.bold()
|
||||||
|
Text(storeApp.developerName)
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
Section {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
ForEach(1...5) { rating in
|
||||||
|
SwiftUI.Button {
|
||||||
|
self.currentRating = rating
|
||||||
|
} label: {
|
||||||
|
Image(systemSymbol: rating > self.currentRating ? .star : .starFill)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
.frame(maxHeight: 40)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.foregroundColor(.yellow)
|
||||||
|
} header: {
|
||||||
|
Text("Rate the App")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Review
|
||||||
|
Section {
|
||||||
|
TextEditor(text: self.$reviewText)
|
||||||
|
.frame(minHeight: 100, maxHeight: 250)
|
||||||
|
} header: {
|
||||||
|
Text("Leave a Review (optional)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Write a Review")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
SwiftUI.Button("Cancel", action: self.dismiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
|
SwiftUI.Button("Send", action: self.sendReview)
|
||||||
|
.disabled(!self.canSendReview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func sendReview() {
|
||||||
|
NotificationManager.shared.showNotification(title: "Feature not Implemented")
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WriteAppReviewView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
WriteAppReviewView(storeApp: app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,11 +38,9 @@ struct MyAppsView: View {
|
|||||||
var viewModel = MyAppsViewModel()
|
var viewModel = MyAppsViewModel()
|
||||||
|
|
||||||
// TODO: Refactor
|
// TODO: Refactor
|
||||||
@State var isShowingFilePicker: Bool = false
|
@State var isRefreshingAllApps: Bool = false
|
||||||
@State var selectedSideloadingIpaURL: URL?
|
@State var selectedSideloadingIpaURL: URL?
|
||||||
|
|
||||||
@State var isShowingAppIDsView: Bool = false
|
|
||||||
|
|
||||||
var remainingAppIDs: Int {
|
var remainingAppIDs: Int {
|
||||||
guard let team = DatabaseManager.shared.activeTeam() else {
|
guard let team = DatabaseManager.shared.activeTeam() else {
|
||||||
return 0
|
return 0
|
||||||
@@ -89,11 +87,14 @@ struct MyAppsView: View {
|
|||||||
Text(L10n.MyAppsView.active)
|
Text(L10n.MyAppsView.active)
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.bold()
|
.bold()
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
SwiftUI.Button {
|
|
||||||
|
if !self.isRefreshingAllApps {
|
||||||
} label: {
|
SwiftUI.Button(L10n.MyAppsView.refreshAll, action: self.refreshAllApps)
|
||||||
Text(L10n.MyAppsView.refreshAll)
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
.progressViewStyle(CircularProgressViewStyle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +119,7 @@ struct MyAppsView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
SwiftUI.Button {
|
ModalNavigationLink(L10n.MyAppsView.viewAppIDs) {
|
||||||
self.isShowingAppIDsView = true
|
|
||||||
} label: {
|
|
||||||
Text(L10n.MyAppsView.viewAppIDs)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: self.$isShowingAppIDsView) {
|
|
||||||
NavigationView {
|
NavigationView {
|
||||||
AppIDsView()
|
AppIDsView()
|
||||||
}
|
}
|
||||||
@@ -137,16 +133,13 @@ struct MyAppsView: View {
|
|||||||
.navigationTitle(L10n.MyAppsView.myApps)
|
.navigationTitle(L10n.MyAppsView.myApps)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
SwiftUI.Button {
|
ModalNavigationLink {
|
||||||
self.isShowingFilePicker = true
|
DocumentPicker(selectedUrl: $selectedSideloadingIpaURL, supportedTypes: sideloadFileTypes)
|
||||||
|
.ignoresSafeArea()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemSymbol: .plus)
|
Image(systemSymbol: .plus)
|
||||||
.imageScale(.large)
|
.imageScale(.large)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: self.$isShowingFilePicker) {
|
|
||||||
DocumentPicker(selectedUrl: $selectedSideloadingIpaURL, supportedTypes: sideloadFileTypes)
|
|
||||||
.ignoresSafeArea()
|
|
||||||
}
|
|
||||||
.onChange(of: self.selectedSideloadingIpaURL) { newValue in
|
.onChange(of: self.selectedSideloadingIpaURL) { newValue in
|
||||||
guard let url = newValue else {
|
guard let url = newValue else {
|
||||||
return
|
return
|
||||||
@@ -205,8 +198,11 @@ struct MyAppsView: View {
|
|||||||
|
|
||||||
func refreshAllApps() {
|
func refreshAllApps() {
|
||||||
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
|
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
|
||||||
|
|
||||||
self.refresh(installedApps) { result in }
|
self.isRefreshingAllApps = true
|
||||||
|
self.refresh(installedApps) { result in
|
||||||
|
self.isRefreshingAllApps = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dismissUpdatesHint(forever: Bool) {
|
func dismissUpdatesHint(forever: Bool) {
|
||||||
@@ -218,7 +214,7 @@ struct MyAppsView: View {
|
|||||||
|
|
||||||
|
|
||||||
extension MyAppsView {
|
extension MyAppsView {
|
||||||
// TODO: Convert to async
|
// TODO: Convert to async?
|
||||||
func refresh(_ apps: [InstalledApp], completionHandler: @escaping ([String : Result<InstalledApp, Error>]) -> Void) {
|
func refresh(_ apps: [InstalledApp], completionHandler: @escaping ([String : Result<InstalledApp, Error>]) -> Void) {
|
||||||
let group = AppManager.shared.refresh(apps, presentingViewController: nil, group: self.viewModel.refreshGroup)
|
let group = AppManager.shared.refresh(apps, presentingViewController: nil, group: self.viewModel.refreshGroup)
|
||||||
|
|
||||||
@@ -248,10 +244,10 @@ extension MyAppsView {
|
|||||||
|
|
||||||
NotificationManager.shared.showNotification(title: title, detailText: message)
|
NotificationManager.shared.showNotification(title: title, detailText: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.viewModel.refreshGroup = nil
|
||||||
|
completionHandler(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.viewModel.refreshGroup = nil
|
|
||||||
completionHandler(results)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.viewModel.refreshGroup = group
|
self.viewModel.refreshGroup = group
|
||||||
@@ -429,6 +425,10 @@ extension MyAppsView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct MyAppsView_Previews: PreviewProvider {
|
struct MyAppsView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static let context = DatabaseManager.shared.viewContext
|
||||||
|
static let app = StoreApp.makeAltStoreApp(in: context)
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
MyAppsView()
|
MyAppsView()
|
||||||
|
|||||||
@@ -30,15 +30,13 @@ struct SettingsView: View {
|
|||||||
var isDevModeEnabled: Bool = false
|
var isDevModeEnabled: Bool = false
|
||||||
|
|
||||||
@State var isShowingConnectAppleIDView = false
|
@State var isShowingConnectAppleIDView = false
|
||||||
@State var isShowingAddShortcutView = false
|
|
||||||
@State var isShowingFeedbackMailView = false
|
|
||||||
@State var isShowingResetPairingFileConfirmation = false
|
@State var isShowingResetPairingFileConfirmation = false
|
||||||
@State var isShowingDevModePrompt = false
|
@State var isShowingDevModePrompt = false
|
||||||
@State var isShowingDevModeMenu = false
|
@State var isShowingDevModeMenu = false
|
||||||
|
|
||||||
@State var externalURLToShow: URL?
|
@State var externalURLToShow: URL?
|
||||||
|
|
||||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown Version"
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@@ -103,13 +101,8 @@ struct SettingsView: View {
|
|||||||
Toggle(isOn: self.$isBackgroundRefreshEnabled, label: {
|
Toggle(isOn: self.$isBackgroundRefreshEnabled, label: {
|
||||||
Text(L10n.SettingsView.backgroundRefresh)
|
Text(L10n.SettingsView.backgroundRefresh)
|
||||||
})
|
})
|
||||||
|
|
||||||
SwiftUI.Button {
|
ModalNavigationLink(L10n.SettingsView.addToSiri) {
|
||||||
self.isShowingAddShortcutView = true
|
|
||||||
} label: {
|
|
||||||
Text(L10n.SettingsView.addToSiri)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: self.$isShowingAddShortcutView) {
|
|
||||||
if let shortcut = INShortcut(intent: INInteraction.refreshAllApps().intent) {
|
if let shortcut = INShortcut(intent: INInteraction.refreshAllApps().intent) {
|
||||||
SiriShortcutSetupView(shortcut: shortcut)
|
SiriShortcutSetupView(shortcut: shortcut)
|
||||||
}
|
}
|
||||||
@@ -169,10 +162,7 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if MailComposeView.canSendMail {
|
if MailComposeView.canSendMail {
|
||||||
SwiftUI.Button("Send Feedback") {
|
ModalNavigationLink("Send Feedback") {
|
||||||
self.isShowingFeedbackMailView = true
|
|
||||||
}
|
|
||||||
.sheet(isPresented: self.$isShowingFeedbackMailView) {
|
|
||||||
MailComposeView(recipients: ["support@sidestore.io"],
|
MailComposeView(recipients: ["support@sidestore.io"],
|
||||||
subject: "SideStore Beta \(appVersion) Feedback") {
|
subject: "SideStore Beta \(appVersion) Feedback") {
|
||||||
NotificationManager.shared.showNotification(title: "Thank you for your feedback!")
|
NotificationManager.shared.showNotification(title: "Thank you for your feedback!")
|
||||||
|
|||||||
@@ -342,25 +342,31 @@ public extension StoreApp
|
|||||||
class func makeAltStoreApp(in context: NSManagedObjectContext) -> StoreApp
|
class func makeAltStoreApp(in context: NSManagedObjectContext) -> StoreApp
|
||||||
{
|
{
|
||||||
let app = StoreApp(context: context)
|
let app = StoreApp(context: context)
|
||||||
|
app.source = Source.makeAltStoreSource(in: context)
|
||||||
app.name = "SideStore"
|
app.name = "SideStore"
|
||||||
app.bundleIdentifier = StoreApp.altstoreAppID
|
app.bundleIdentifier = StoreApp.altstoreAppID
|
||||||
app.developerName = "Side Team"
|
app.developerName = "Side Team"
|
||||||
app.localizedDescription = "SideStore is an alternative App Store."
|
app.localizedDescription = "SideStore is an alternative App Store."
|
||||||
app.iconURL = URL(string: "https://user-images.githubusercontent.com/705880/63392210-540c5980-c37b-11e9-968c-8742fc68ab2e.png")!
|
app.iconURL = URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/icon.png")!
|
||||||
app.screenshotURLs = []
|
app.screenshotURLs = [
|
||||||
app.sourceIdentifier = Source.altStoreIdentifier
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/browse-dark.png")!,
|
||||||
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/apps-dark.png")!,
|
||||||
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/news-dark.png")!,
|
||||||
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/browse-light.png")!,
|
||||||
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/apps-light.png")!,
|
||||||
|
URL(string: "https://apps.sidestore.io/apps/sidestore/v0.1.1/news-light.png")!,
|
||||||
|
]
|
||||||
|
app.tintColor = UIColor(named: "AccentColor")
|
||||||
|
|
||||||
let appVersion = AppVersion.makeAppVersion(version: "0.3.0",
|
let appVersion = AppVersion.makeAppVersion(version: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown",
|
||||||
date: Date(),
|
date: Date(),
|
||||||
downloadURL: URL(string: "http://rileytestut.com")!,
|
downloadURL: URL(string: "https://github.com/SideStore/SideStore/releases/download/0.1.1/SideStore.ipa")!,
|
||||||
size: 0,
|
size: 0,
|
||||||
appBundleID: app.bundleIdentifier,
|
appBundleID: app.bundleIdentifier,
|
||||||
sourceID: Source.altStoreIdentifier,
|
sourceID: Source.altStoreIdentifier,
|
||||||
in: context)
|
in: context)
|
||||||
app.setVersions([appVersion])
|
app.setVersions([appVersion])
|
||||||
|
|
||||||
print("makeAltStoreApp StoreApp: \(String(describing: app))")
|
|
||||||
|
|
||||||
#if BETA
|
#if BETA
|
||||||
app.isBeta = true
|
app.isBeta = true
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user