[ADD] UI for writing an app review and submit an app rating

This commit is contained in:
Fabian Thies
2023-02-19 14:31:01 +01:00
parent 2211013e57
commit 89609ad35c
3 changed files with 145 additions and 18 deletions

View File

@@ -33,6 +33,7 @@
1F1295812989B51F0048FCB9 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 1F1295802989B51F0048FCB9 /* ExpandableText */; };
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 */; };
1F1D669E29A234CE0095BFCD /* WriteAppReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1D669D29A234CE0095BFCD /* WriteAppReviewView.swift */; };
1F2EF787297C4D40002FD839 /* LicensesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2EF786297C4D40002FD839 /* LicensesView.swift */; };
1F44634529744E570070E514 /* HintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F44634429744E570070E514 /* HintView.swift */; };
1F545E83298D79E400589F68 /* ErrorLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F545E82298D79E400589F68 /* ErrorLogView.swift */; };
@@ -585,6 +586,7 @@
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>"; };
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>"; };
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>"; };
@@ -1179,6 +1181,7 @@
1F0DD8202933B749007608A4 /* AppPermissionsGrid.swift */,
1F07F56A2955F11500F7BE95 /* AppScreenshotsPreview.swift */,
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */,
1F1D669D29A234CE0095BFCD /* WriteAppReviewView.swift */,
);
path = "App Detail";
sourceTree = "<group>";
@@ -2889,6 +2892,7 @@
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
1F1D669E29A234CE0095BFCD /* WriteAppReviewView.swift in Sources */,
1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */,
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */,
1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */,

View File

@@ -25,7 +25,7 @@ struct AppDetailView: View {
let maxContentCornerRadius: CGFloat = 24
let headerViewHeight: CGFloat = 140
let permissionColumns = 4
var headerBlurRadius: CGFloat {
min(20, max(0, 20 - (scrollOffset / -150) * 20))
}
@@ -35,6 +35,11 @@ struct AppDetailView: View {
var contentCornerRadius: CGFloat {
max(CGFloat.zero, min(maxContentCornerRadius, maxContentCornerRadius * (1 - self.scrollOffset / self.headerViewHeight)))
}
var canRateApp: Bool {
self.storeApp.installedApp != nil
}
var body: some View {
ObservableScrollView(scrollOffset: $scrollOffset) { proxy in
@@ -86,7 +91,7 @@ struct AppDetailView: View {
}
var contentView: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 24) {
VStack(alignment: .leading, spacing: 32) {
if storeApp.isFromOfficialSource {
officialAppBadge
@@ -145,7 +150,7 @@ struct AppDetailView: View {
}
VStack(spacing: 16) {
VStack(spacing: 24) {
Divider()
currentVersionView
@@ -179,23 +184,29 @@ struct AppDetailView: View {
}
var officialAppBadge: some View {
HStack {
Spacer()
Image(systemSymbol: .checkmarkSealFill)
Text(L10n.AppDetailView.Badge.official)
Spacer()
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
HStack {
Spacer()
Image(systemSymbol: .checkmarkSealFill)
Text(L10n.AppDetailView.Badge.official)
Spacer()
}
.foregroundColor(.accentColor)
}
.foregroundColor(.accentColor)
.padding(.horizontal)
}
var trustedAppBadge: some View {
HStack {
Spacer()
Image(systemSymbol: .shieldLefthalfFill)
Text(L10n.AppDetailView.Badge.trusted)
Spacer()
HintView(backgroundColor: Color(UIColor.secondarySystemBackground)) {
HStack {
Spacer()
Image(systemSymbol: .shieldLefthalfFill)
Text(L10n.AppDetailView.Badge.trusted)
Spacer()
}
.foregroundColor(.accentColor)
}
.foregroundColor(.accentColor)
.padding(.horizontal)
}
var currentVersionView: some View {
@@ -318,7 +329,7 @@ struct AppDetailView: View {
.foregroundColor(.secondary)
}
RatingStars(rating: i + 1)
RatingStars(rating: 5 - i)
.frame(height: 12)
.foregroundColor(.yellow)
}
@@ -337,6 +348,17 @@ struct AppDetailView: View {
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.frame(height: 150)
.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 {
AppPermissionsGrid(permissions: storeApp.permissions)
}
Spacer()
}
.frame(maxWidth: .infinity, alignment: .leading)
}
var informationData: [(title: String, content: String)] {

View 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)
}
}
}