mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Create MainWindow to split up XcodeListView
This commit is contained in:
parent
9dc3d21f2e
commit
8084f057fd
7 changed files with 156 additions and 121 deletions
|
|
@ -69,6 +69,9 @@
|
||||||
CAD2E7A62449575000113D76 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A52449575000113D76 /* Assets.xcassets */; };
|
CAD2E7A62449575000113D76 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A52449575000113D76 /* Assets.xcassets */; };
|
||||||
CAD2E7A92449575000113D76 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A82449575000113D76 /* Preview Assets.xcassets */; };
|
CAD2E7A92449575000113D76 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A82449575000113D76 /* Preview Assets.xcassets */; };
|
||||||
CAD2E7B82449575100113D76 /* XcodesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD2E7B72449575100113D76 /* XcodesTests.swift */; };
|
CAD2E7B82449575100113D76 /* XcodesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD2E7B72449575100113D76 /* XcodesTests.swift */; };
|
||||||
|
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE4247E259A666100B8B246 /* MainWindow.swift */; };
|
||||||
|
CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE42486259A68A300B8B246 /* XcodeListCategory.swift */; };
|
||||||
|
CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */; };
|
||||||
CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */; };
|
CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */; };
|
||||||
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB942598FE96003DCC5A /* FocusedValues.swift */; };
|
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB942598FE96003DCC5A /* FocusedValues.swift */; };
|
||||||
CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */; };
|
CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */; };
|
||||||
|
|
@ -189,6 +192,9 @@
|
||||||
CAD2E7B32449575100113D76 /* XcodesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
CAD2E7B32449575100113D76 /* XcodesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
CAD2E7B72449575100113D76 /* XcodesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodesTests.swift; sourceTree = "<group>"; };
|
CAD2E7B72449575100113D76 /* XcodesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodesTests.swift; sourceTree = "<group>"; };
|
||||||
CAD2E7B92449575100113D76 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
CAD2E7B92449575100113D76 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
CAE4247E259A666100B8B246 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
||||||
|
CAE42486259A68A300B8B246 /* XcodeListCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListCategory.swift; sourceTree = "<group>"; };
|
||||||
|
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+IsNotNil.swift"; sourceTree = "<group>"; };
|
||||||
CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedXcode.swift; sourceTree = "<group>"; };
|
CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedXcode.swift; sourceTree = "<group>"; };
|
||||||
CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = "<group>"; };
|
CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = "<group>"; };
|
||||||
CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = SimpleXPCApp.LICENSE; sourceTree = "<group>"; };
|
CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = SimpleXPCApp.LICENSE; sourceTree = "<group>"; };
|
||||||
|
|
@ -302,6 +308,7 @@
|
||||||
CAFBDC67259A308B003DCC5A /* InspectorPane.swift */,
|
CAFBDC67259A308B003DCC5A /* InspectorPane.swift */,
|
||||||
CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */,
|
CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */,
|
||||||
CA44901E2463AD34003D8213 /* Tag.swift */,
|
CA44901E2463AD34003D8213 /* Tag.swift */,
|
||||||
|
CAE42486259A68A300B8B246 /* XcodeListCategory.swift */,
|
||||||
CAD2E7A32449574E00113D76 /* XcodeListView.swift */,
|
CAD2E7A32449574E00113D76 /* XcodeListView.swift */,
|
||||||
);
|
);
|
||||||
path = XcodeList;
|
path = XcodeList;
|
||||||
|
|
@ -326,6 +333,7 @@
|
||||||
CA9FF8F425959CE000E47BAF /* HelperInstaller.swift */,
|
CA9FF8F425959CE000E47BAF /* HelperInstaller.swift */,
|
||||||
CA9FF9352595B44700E47BAF /* HelperClient.swift */,
|
CA9FF9352595B44700E47BAF /* HelperClient.swift */,
|
||||||
CA9FF8862595607900E47BAF /* InstalledXcode.swift */,
|
CA9FF8862595607900E47BAF /* InstalledXcode.swift */,
|
||||||
|
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */,
|
||||||
CABFA9AE2592EEE900380FEE /* Path+.swift */,
|
CABFA9AE2592EEE900380FEE /* Path+.swift */,
|
||||||
CABFA9B42592EEEA00380FEE /* Process.swift */,
|
CABFA9B42592EEEA00380FEE /* Process.swift */,
|
||||||
CABFA9B02592EEEA00380FEE /* Promise+.swift */,
|
CABFA9B02592EEEA00380FEE /* Promise+.swift */,
|
||||||
|
|
@ -348,6 +356,7 @@
|
||||||
CA9FF8552595082000E47BAF /* About */,
|
CA9FF8552595082000E47BAF /* About */,
|
||||||
CAA1CB50255A5D16003FD669 /* SignIn */,
|
CAA1CB50255A5D16003FD669 /* SignIn */,
|
||||||
CABFAA142592F73000380FEE /* XcodeList */,
|
CABFAA142592F73000380FEE /* XcodeList */,
|
||||||
|
CAE4247E259A666100B8B246 /* MainWindow.swift */,
|
||||||
CABFAA2A2592FBFC00380FEE /* SettingsView.swift */,
|
CABFAA2A2592FBFC00380FEE /* SettingsView.swift */,
|
||||||
CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */,
|
CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */,
|
||||||
CA9FF8652595130600E47BAF /* View+IsHidden.swift */,
|
CA9FF8652595130600E47BAF /* View+IsHidden.swift */,
|
||||||
|
|
@ -613,6 +622,7 @@
|
||||||
CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */,
|
CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */,
|
||||||
CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */,
|
CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */,
|
||||||
CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */,
|
CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */,
|
||||||
|
CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */,
|
||||||
CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */,
|
CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */,
|
||||||
CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */,
|
CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */,
|
||||||
CA44901F2463AD34003D8213 /* Tag.swift in Sources */,
|
CA44901F2463AD34003D8213 /* Tag.swift in Sources */,
|
||||||
|
|
@ -627,6 +637,7 @@
|
||||||
CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */,
|
CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */,
|
||||||
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
||||||
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
|
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
|
||||||
|
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
|
||||||
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
|
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
|
||||||
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
|
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
|
||||||
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */,
|
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */,
|
||||||
|
|
@ -645,6 +656,7 @@
|
||||||
CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */,
|
CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */,
|
||||||
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */,
|
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */,
|
||||||
CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */,
|
CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */,
|
||||||
|
CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */,
|
||||||
CABFAA2C2592FBFC00380FEE /* SettingsView.swift in Sources */,
|
CABFAA2C2592FBFC00380FEE /* SettingsView.swift in Sources */,
|
||||||
CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */,
|
CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */,
|
||||||
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
|
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
|
||||||
|
|
|
||||||
9
Xcodes/Backend/Optional+IsNotNil.swift
Normal file
9
Xcodes/Backend/Optional+IsNotNil.swift
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Optional {
|
||||||
|
/// Note that this is lossy when setting, so you can really only set it to nil, but this is sufficient for mapping `Binding<Item?>` to `Binding<Bool>` for Alerts, Popovers, etc.
|
||||||
|
var isNotNil: Bool {
|
||||||
|
get { self != nil }
|
||||||
|
set { self = newValue ? self : nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
74
Xcodes/Frontend/MainWindow.swift
Normal file
74
Xcodes/Frontend/MainWindow.swift
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MainWindow: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@State private var selection: Xcode.ID?
|
||||||
|
@State private var searchText: String = ""
|
||||||
|
@AppStorage("lastUpdated") private var lastUpdated: Double?
|
||||||
|
@SceneStorage("isShowingInfoPane") private var isShowingInfoPane = false
|
||||||
|
@SceneStorage("xcodeListCategory") private var category: XcodeListCategory = .all
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HSplitView {
|
||||||
|
XcodeListView(searchText: searchText, category: category)
|
||||||
|
.frame(minWidth: 300)
|
||||||
|
.layoutPriority(1)
|
||||||
|
|
||||||
|
InspectorPane()
|
||||||
|
.frame(minWidth: 300, maxWidth: .infinity)
|
||||||
|
.frame(width: isShowingInfoPane ? nil : 0)
|
||||||
|
.isHidden(!isShowingInfoPane)
|
||||||
|
}
|
||||||
|
.mainToolbar(
|
||||||
|
category: $category,
|
||||||
|
isShowingInfoPane: $isShowingInfoPane,
|
||||||
|
searchText: $searchText
|
||||||
|
)
|
||||||
|
.navigationSubtitle(subtitleText)
|
||||||
|
.frame(minWidth: 600, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
|
||||||
|
.alert(item: $appState.error) { error in
|
||||||
|
Alert(title: Text(error.title),
|
||||||
|
message: Text(verbatim: error.message),
|
||||||
|
dismissButton: .default(Text("OK")))
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Removing this for now, because it's overriding the error alert that's being worked on above.
|
||||||
|
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
|
||||||
|
Alert(title: Text("Uninstall Xcode \(xcode.description)?"),
|
||||||
|
message: Text("It will be moved to the Trash, but won't be emptied."),
|
||||||
|
primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(id: xcode.id) }),
|
||||||
|
secondaryButton: .cancel(Text("Cancel")))
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
.sheet(isPresented: $appState.secondFactorData.isNotNil) {
|
||||||
|
secondFactorView(appState.secondFactorData!)
|
||||||
|
.environmentObject(appState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var subtitleText: Text {
|
||||||
|
if let lastUpdated = lastUpdated.map(Date.init(timeIntervalSince1970:)) {
|
||||||
|
return Text("Updated at \(lastUpdated, style: .date) \(lastUpdated, style: .time)")
|
||||||
|
} else {
|
||||||
|
return Text("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func secondFactorView(_ secondFactorData: AppState.SecondFactorData) -> some View {
|
||||||
|
switch secondFactorData.option {
|
||||||
|
case .codeSent:
|
||||||
|
SignIn2FAView(isPresented: $appState.secondFactorData.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
||||||
|
case .smsSent(let trustedPhoneNumber):
|
||||||
|
SignInSMSView(isPresented: $appState.secondFactorData.isNotNil, trustedPhoneNumber: trustedPhoneNumber, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
||||||
|
case .smsPendingChoice:
|
||||||
|
SignInPhoneListView(isPresented: $appState.secondFactorData.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MainWindow_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MainWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
||||||
|
|
||||||
struct MainToolbarModifier: ViewModifier {
|
struct MainToolbarModifier: ViewModifier {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@Binding var category: XcodeListView.Category
|
@Binding var category: XcodeListCategory
|
||||||
@Binding var isShowingInfoPane: Bool
|
@Binding var isShowingInfoPane: Bool
|
||||||
@Binding var searchText: String
|
@Binding var searchText: String
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ struct MainToolbarModifier: ViewModifier {
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
func mainToolbar(
|
func mainToolbar(
|
||||||
category: Binding<XcodeListView.Category>,
|
category: Binding<XcodeListCategory>,
|
||||||
isShowingInfoPane: Binding<Bool>,
|
isShowingInfoPane: Binding<Bool>,
|
||||||
searchText: Binding<String>
|
searchText: Binding<String>
|
||||||
) -> some View {
|
) -> some View {
|
||||||
|
|
|
||||||
15
Xcodes/Frontend/XcodeList/XcodeListCategory.swift
Normal file
15
Xcodes/Frontend/XcodeList/XcodeListCategory.swift
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConvertible {
|
||||||
|
case all
|
||||||
|
case installed
|
||||||
|
|
||||||
|
var id: Self { self }
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "All"
|
||||||
|
case .installed: return "Installed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,13 @@ import PromiseKit
|
||||||
|
|
||||||
struct XcodeListView: View {
|
struct XcodeListView: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@State private var selection: Xcode.ID?
|
private let searchText: String
|
||||||
@State private var searchText: String = ""
|
private let category: XcodeListCategory
|
||||||
@AppStorage("lastUpdated") private var lastUpdated: Double?
|
|
||||||
@SceneStorage("isShowingInfoPane") private var isShowingInfoPane = false
|
init(searchText: String, category: XcodeListCategory) {
|
||||||
@SceneStorage("xcodeListCategory") private var category: Category = .all
|
self.searchText = searchText
|
||||||
|
self.category = category
|
||||||
|
}
|
||||||
|
|
||||||
var visibleXcodes: [Xcode] {
|
var visibleXcodes: [Xcode] {
|
||||||
var xcodes: [Xcode]
|
var xcodes: [Xcode]
|
||||||
|
|
@ -26,114 +28,46 @@ struct XcodeListView: View {
|
||||||
return xcodes
|
return xcodes
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Category: String, CaseIterable, Identifiable, CustomStringConvertible {
|
|
||||||
case all
|
|
||||||
case installed
|
|
||||||
|
|
||||||
var id: Self { self }
|
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .all: return "All"
|
|
||||||
case .installed: return "Installed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HSplitView {
|
List(visibleXcodes, selection: $appState.selectedXcodeID) { xcode in
|
||||||
List(visibleXcodes, selection: $appState.selectedXcodeID) { xcode in
|
HStack {
|
||||||
HStack {
|
appIconView(for: xcode)
|
||||||
appIconView(for: xcode)
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(xcode.description)
|
||||||
|
.font(.body)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
Text(verbatim: xcode.path ?? "")
|
||||||
Text(xcode.description)
|
.font(.caption)
|
||||||
.font(.body)
|
.foregroundColor(appState.selectedXcodeID == xcode.id ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor))
|
||||||
|
|
||||||
Text(verbatim: xcode.path ?? "")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(appState.selectedXcodeID == xcode.id ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor))
|
|
||||||
}
|
|
||||||
|
|
||||||
if xcode.selected {
|
|
||||||
Tag(text: "SELECTED")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(xcode.installed ? "INSTALLED" : "INSTALL") {
|
|
||||||
print("Installing...")
|
|
||||||
}
|
|
||||||
.buttonStyle(AppStoreButtonStyle(installed: xcode.installed,
|
|
||||||
highlighted: appState.selectedXcodeID == xcode.id))
|
|
||||||
.disabled(xcode.installed)
|
|
||||||
}
|
}
|
||||||
.contextMenu {
|
|
||||||
InstallButton(xcode: xcode)
|
if xcode.selected {
|
||||||
|
Tag(text: "SELECTED")
|
||||||
Divider()
|
.foregroundColor(.green)
|
||||||
|
}
|
||||||
if xcode.installed {
|
|
||||||
SelectButton(xcode: xcode)
|
Spacer()
|
||||||
OpenButton(xcode: xcode)
|
|
||||||
RevealButton(xcode: xcode)
|
Button(xcode.installed ? "INSTALLED" : "INSTALL") {
|
||||||
CopyPathButton(xcode: xcode)
|
print("Installing...")
|
||||||
}
|
}
|
||||||
|
.buttonStyle(AppStoreButtonStyle(installed: xcode.installed,
|
||||||
|
highlighted: appState.selectedXcodeID == xcode.id))
|
||||||
|
.disabled(xcode.installed)
|
||||||
|
}
|
||||||
|
.contextMenu {
|
||||||
|
InstallButton(xcode: xcode)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
if xcode.installed {
|
||||||
|
SelectButton(xcode: xcode)
|
||||||
|
OpenButton(xcode: xcode)
|
||||||
|
RevealButton(xcode: xcode)
|
||||||
|
CopyPathButton(xcode: xcode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minWidth: 300)
|
|
||||||
.layoutPriority(1)
|
|
||||||
|
|
||||||
InspectorPane()
|
|
||||||
.frame(minWidth: 300, maxWidth: .infinity)
|
|
||||||
.frame(width: isShowingInfoPane ? nil : 0)
|
|
||||||
.isHidden(!isShowingInfoPane)
|
|
||||||
}
|
|
||||||
.mainToolbar(
|
|
||||||
category: $category,
|
|
||||||
isShowingInfoPane: $isShowingInfoPane,
|
|
||||||
searchText: $searchText
|
|
||||||
)
|
|
||||||
.navigationSubtitle(subtitleText)
|
|
||||||
.frame(minWidth: 200, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
|
|
||||||
.alert(item: $appState.error) { error in
|
|
||||||
Alert(title: Text(error.title),
|
|
||||||
message: Text(verbatim: error.message),
|
|
||||||
dismissButton: .default(Text("OK")))
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Removing this for now, because it's overriding the error alert that's being worked on above.
|
|
||||||
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
|
|
||||||
Alert(title: Text("Uninstall Xcode \(xcode.description)?"),
|
|
||||||
message: Text("It will be moved to the Trash, but won't be emptied."),
|
|
||||||
primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(id: xcode.id) }),
|
|
||||||
secondaryButton: .cancel(Text("Cancel")))
|
|
||||||
}
|
|
||||||
**/
|
|
||||||
.sheet(isPresented: $appState.secondFactorData.isNotNil) {
|
|
||||||
secondFactorView(appState.secondFactorData!)
|
|
||||||
.environmentObject(appState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
func secondFactorView(_ secondFactorData: AppState.SecondFactorData) -> some View {
|
|
||||||
switch secondFactorData.option {
|
|
||||||
case .codeSent:
|
|
||||||
SignIn2FAView(isPresented: $appState.secondFactorData.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
|
||||||
case .smsSent(let trustedPhoneNumber):
|
|
||||||
SignInSMSView(isPresented: $appState.secondFactorData.isNotNil, trustedPhoneNumber: trustedPhoneNumber, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
|
||||||
case .smsPendingChoice:
|
|
||||||
SignInPhoneListView(isPresented: $appState.secondFactorData.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var subtitleText: Text {
|
|
||||||
if let lastUpdated = lastUpdated.map(Date.init(timeIntervalSince1970:)) {
|
|
||||||
return Text("Updated at \(lastUpdated, style: .date) \(lastUpdated, style: .time)")
|
|
||||||
} else {
|
|
||||||
return Text("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +86,7 @@ struct XcodeListView: View {
|
||||||
struct XcodeListView_Previews: PreviewProvider {
|
struct XcodeListView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Group {
|
Group {
|
||||||
XcodeListView()
|
XcodeListView(searchText: "", category: .all)
|
||||||
.environmentObject({ () -> AppState in
|
.environmentObject({ () -> AppState in
|
||||||
let a = AppState()
|
let a = AppState()
|
||||||
a.allXcodes = [
|
a.allXcodes = [
|
||||||
|
|
@ -167,11 +101,3 @@ struct XcodeListView_Previews: PreviewProvider {
|
||||||
.previewLayout(.sizeThatFits)
|
.previewLayout(.sizeThatFits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Optional {
|
|
||||||
/// Note that this is lossy when setting, so you can really only set it to nil, but this is sufficient for mapping `Binding<Item?>` to `Binding<Bool>` for Alerts, Popovers, etc.
|
|
||||||
var isNotNil: Bool {
|
|
||||||
get { self != nil }
|
|
||||||
set { self = newValue ? self : nil }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ struct XcodesApp: App {
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup("Xcodes") {
|
WindowGroup("Xcodes") {
|
||||||
XcodeListView()
|
MainWindow()
|
||||||
.frame(minWidth: 600)
|
|
||||||
.environmentObject(appState)
|
.environmentObject(appState)
|
||||||
// This is intentionally used on a View, and not on a WindowGroup,
|
// This is intentionally used on a View, and not on a WindowGroup,
|
||||||
// so that it's triggered when an individual window's phase changes instead of all window phases.
|
// so that it's triggered when an individual window's phase changes instead of all window phases.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue