// // BrowseView.swift // SideStoreUI // // Created by Fabian Thies on 18.11.22. // Copyright © 2022 Fabian Thies. All rights reserved. // import SwiftUI import SFSafeSymbols import AltStoreCore struct BrowseView: View { @SwiftUI.FetchRequest(sortDescriptors: [ NSSortDescriptor(keyPath: \StoreApp.sourceIdentifier, ascending: true), NSSortDescriptor(keyPath: \StoreApp.sortIndex, ascending: true), NSSortDescriptor(keyPath: \StoreApp.name, ascending: true), NSSortDescriptor(keyPath: \StoreApp.bundleIdentifier, ascending: true) ], predicate: NSPredicate(format: "%K != %@", #keyPath(StoreApp.bundleIdentifier), StoreApp.altstoreAppID)) var apps: FetchedResults var filteredApps: [StoreApp] { apps.items(matching: self.searchText) } @State var selectedStoreApp: StoreApp? @State var searchText = "" @State var isShowingSourcesView = false var body: some View { ScrollView { VStack(alignment: .leading) { if searchText.isEmpty { VStack(alignment: .leading, spacing: 32) { promotedCategoriesView Text(L10n.BrowseView.Section.AllApps.title) .font(.title2) .bold() } } if searchText.isEmpty, filteredApps.count == 0 { HintView { Text("You don't have any apps yet.") .bold() Text("Apps are provided by \"sources\". The specification for them is an open standard, so everyone can create their own source. To get you started, we have compiled a list of \"Trusted Sources\" which you can check out by tapping the button below.") .font(.callout) .foregroundColor(.secondary) SwiftUI.Button { self.isShowingSourcesView = true } label: { Label("Add Source", systemSymbol: .plus) } .buttonStyle(FilledButtonStyle()) .padding(.top, 8) } } else { LazyVStack(spacing: 32) { ForEach(filteredApps, id: \.bundleIdentifier) { app in NavigationLink { AppDetailView(storeApp: app) } label: { BrowseAppPreviewView(storeApp: app) } .buttonStyle(PlainButtonStyle()) } } } } .padding() .searchable(text: self.$searchText, placeholder: L10n.BrowseView.search) } .background(Color(UIColor.systemGroupedBackground).ignoresSafeArea()) .navigationTitle(L10n.BrowseView.title) .toolbar { ToolbarItem(placement: .navigationBarLeading) { SwiftUI.Button { self.isShowingSourcesView = true } label: { Text(L10n.BrowseView.Actions.sources) } .sheet(isPresented: self.$isShowingSourcesView) { NavigationView { SourcesView() } } } ToolbarItem(placement: .navigationBarTrailing) { SwiftUI.Button { } label: { Image(systemSymbol: .lineHorizontal3DecreaseCircle) .imageScale(.large) } } } } var promotedCategoriesView: some View { VStack { HStack { Text(L10n.BrowseView.Section.PromotedCategories.title) .font(.title2) .bold() Spacer() SwiftUI.Button(action: {}, label: { Text(L10n.BrowseView.Section.PromotedCategories.showAll) }) .font(.callout) } LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) { PromotedCategoryView() .shadow(color: .black.opacity(0.1), radius: 8, y: 5) PromotedCategoryView() .shadow(color: .black.opacity(0.1), radius: 8, y: 5) } } } } struct BrowseView_Previews: PreviewProvider { static var previews: some View { NavigationView { BrowseView() } } } struct PromotedCategoryView: View { var body: some View { ZStack { GeometryReader { proxy in RadialGradient(colors: [ Color(UIColor(hexString: "477E84")!), Color(UIColor.secondarySystemBackground), Color(UIColor.secondarySystemBackground), Color(UIColor(hexString: "C38FF5")!) ], center: .bottomLeading, startRadius: 0, endRadius: proxy.size.width) } HStack { Image(systemSymbol: .dpadRightFill) Text(L10n.BrowseView.Categories.gamesAndEmulators) .multilineTextAlignment(.leading) } .foregroundColor(.accentColor) .padding() } .aspectRatio(21/9, contentMode: .fill) .frame(maxWidth: .infinity) .clipShape(RoundedRectangle(cornerRadius: 12)) } }