2022-11-23 22:34:02 +01:00
//
// B r o w s e V i e w . s w i f t
// S i d e S t o r e U I
//
// C r e a t e d b y F a b i a n T h i e s o n 1 8 . 1 1 . 2 2 .
// C o p y r i g h t © 2 0 2 2 F a b i a n T h i e s . A l l r i g h t s r e s e r v e d .
//
import SwiftUI
2022-12-23 15:21:16 +01:00
import SFSafeSymbols
2022-11-23 22:34:02 +01:00
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 )
2022-12-12 19:18:57 +01:00
] , predicate : NSPredicate ( format : " %K != %@ " , # keyPath ( StoreApp . bundleIdentifier ) , StoreApp . altstoreAppID ) )
2022-11-23 22:34:02 +01:00
var apps : FetchedResults < StoreApp >
var filteredApps : [ StoreApp ] {
2022-12-12 19:18:57 +01:00
apps . items ( matching : self . searchText )
2022-11-23 22:34:02 +01:00
}
@ State
var selectedStoreApp : StoreApp ?
@ State var searchText = " "
@ State var isShowingSourcesView = false
var body : some View {
ScrollView {
2022-12-21 17:49:49 +01:00
VStack ( alignment : . leading ) {
if searchText . isEmpty {
VStack ( alignment : . leading , spacing : 32 ) {
promotedCategoriesView
2022-12-22 10:29:57 +01:00
Text ( L10n . BrowseView . Section . AllApps . title )
2022-12-21 17:49:49 +01:00
. font ( . title2 )
. bold ( )
}
}
2023-01-16 19:02:58 +01:00
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
2022-12-21 17:49:49 +01:00
} label : {
2023-01-16 19:02:58 +01:00
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 ( ) )
2022-12-21 17:49:49 +01:00
}
2022-11-23 22:34:02 +01:00
}
}
}
. padding ( )
2022-12-22 10:29:57 +01:00
. searchable ( text : self . $ searchText , placeholder : L10n . BrowseView . search )
2022-11-23 22:34:02 +01:00
}
. background ( Color ( UIColor . systemGroupedBackground ) . ignoresSafeArea ( ) )
2022-12-22 10:29:57 +01:00
. navigationTitle ( L10n . BrowseView . title )
2022-11-23 22:34:02 +01:00
. toolbar {
2022-12-21 17:49:49 +01:00
ToolbarItem ( placement : . navigationBarLeading ) {
2022-11-23 22:34:02 +01:00
SwiftUI . Button {
self . isShowingSourcesView = true
} label : {
2022-12-22 10:29:57 +01:00
Text ( L10n . BrowseView . Actions . sources )
2022-11-23 22:34:02 +01:00
}
. sheet ( isPresented : self . $ isShowingSourcesView ) {
NavigationView {
SourcesView ( )
}
}
}
2022-12-21 17:49:49 +01:00
ToolbarItem ( placement : . navigationBarTrailing ) {
SwiftUI . Button {
} label : {
2022-12-23 15:21:16 +01:00
Image ( systemSymbol : . lineHorizontal3DecreaseCircle )
2022-12-21 17:49:49 +01:00
. imageScale ( . large )
}
}
}
}
var promotedCategoriesView : some View {
VStack {
HStack {
2022-12-22 10:29:57 +01:00
Text ( L10n . BrowseView . Section . PromotedCategories . title )
2022-12-21 17:49:49 +01:00
. font ( . title2 )
. bold ( )
Spacer ( )
2022-12-22 10:29:57 +01:00
SwiftUI . Button ( action : { } , label : {
Text ( L10n . BrowseView . Section . PromotedCategories . showAll )
} )
. font ( . callout )
2022-12-21 17:49:49 +01:00
}
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 )
}
2022-11-23 22:34:02 +01:00
}
}
}
struct BrowseView_Previews : PreviewProvider {
static var previews : some View {
2023-01-16 19:02:58 +01:00
NavigationView {
BrowseView ( )
}
2022-11-23 22:34:02 +01:00
}
}
2022-12-21 17:49:49 +01:00
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 {
2022-12-23 15:21:16 +01:00
Image ( systemSymbol : . dpadRightFill )
2022-12-24 21:06:28 -07:00
Text ( L10n . BrowseView . Categories . gamesAndEmulators )
2022-12-21 17:49:49 +01:00
. multilineTextAlignment ( . leading )
}
. foregroundColor ( . accentColor )
. padding ( )
}
. aspectRatio ( 21 / 9 , contentMode : . fill )
. frame ( maxWidth : . infinity )
. clipShape ( RoundedRectangle ( cornerRadius : 12 ) )
}
}