Add copy to clipboard for Firmwares (#119)

This commit is contained in:
Nindi Gill 2024-02-11 17:20:38 +11:00 committed by GitHub
parent e9ad636584
commit 82f5b049e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 102 additions and 23 deletions

View file

@ -93,6 +93,7 @@
398734D028603D9E00B4C357 /* UInt8+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734CF28603D9E00B4C357 /* UInt8+Extension.swift */; }; 398734D028603D9E00B4C357 /* UInt8+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734CF28603D9E00B4C357 /* UInt8+Extension.swift */; };
398734D228603DE700B4C357 /* [UInt8]+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D128603DE700B4C357 /* [UInt8]+Extension.swift */; }; 398734D228603DE700B4C357 /* [UInt8]+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D128603DE700B4C357 /* [UInt8]+Extension.swift */; };
398734D4286046B000B4C357 /* UInt32+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D3286046B000B4C357 /* UInt32+Extension.swift */; }; 398734D4286046B000B4C357 /* UInt32+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D3286046B000B4C357 /* UInt32+Extension.swift */; };
398BE6B52B62450500FE0C29 /* FloatingAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398BE6B42B62450500FE0C29 /* FloatingAlert.swift */; };
39CA25E32941D8BB0030711E /* FileAttributesUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CA25E22941D8BB0030711E /* FileAttributesUpdater.swift */; }; 39CA25E32941D8BB0030711E /* FileAttributesUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CA25E22941D8BB0030711E /* FileAttributesUpdater.swift */; };
39CB5E3D293F5C2E00CFDBB8 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */; }; 39CB5E3D293F5C2E00CFDBB8 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */; };
39CB5E3F2941486D00CFDBB8 /* CatalogSeedType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */; }; 39CB5E3F2941486D00CFDBB8 /* CatalogSeedType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */; };
@ -250,6 +251,7 @@
398734CF28603D9E00B4C357 /* UInt8+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt8+Extension.swift"; sourceTree = "<group>"; }; 398734CF28603D9E00B4C357 /* UInt8+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt8+Extension.swift"; sourceTree = "<group>"; };
398734D128603DE700B4C357 /* [UInt8]+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "[UInt8]+Extension.swift"; sourceTree = "<group>"; }; 398734D128603DE700B4C357 /* [UInt8]+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "[UInt8]+Extension.swift"; sourceTree = "<group>"; };
398734D3286046B000B4C357 /* UInt32+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt32+Extension.swift"; sourceTree = "<group>"; }; 398734D3286046B000B4C357 /* UInt32+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt32+Extension.swift"; sourceTree = "<group>"; };
398BE6B42B62450500FE0C29 /* FloatingAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingAlert.swift; sourceTree = "<group>"; };
39CA25E22941D8BB0030711E /* FileAttributesUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileAttributesUpdater.swift; sourceTree = "<group>"; }; 39CA25E22941D8BB0030711E /* FileAttributesUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileAttributesUpdater.swift; sourceTree = "<group>"; };
39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = "<group>"; }; 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = "<group>"; };
39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogSeedType.swift; sourceTree = "<group>"; }; 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogSeedType.swift; sourceTree = "<group>"; };
@ -538,6 +540,7 @@
children = ( children = (
39252AA2285C3CC400956C74 /* CodesigningPickerView.swift */, 39252AA2285C3CC400956C74 /* CodesigningPickerView.swift */,
39252AA4285C463A00956C74 /* DynamicTextView.swift */, 39252AA4285C463A00956C74 /* DynamicTextView.swift */,
398BE6B42B62450500FE0C29 /* FloatingAlert.swift */,
39252A86285ACE9C00956C74 /* FooterText.swift */, 39252A86285ACE9C00956C74 /* FooterText.swift */,
5795700C2A31B081004C7051 /* MistActionButtonStyle.swift */, 5795700C2A31B081004C7051 /* MistActionButtonStyle.swift */,
39252AA0285C2A1600956C74 /* PaddedDivider.swift */, 39252AA0285C2A1600956C74 /* PaddedDivider.swift */,
@ -843,6 +846,7 @@
39FF05F62859850F00A86670 /* SettingsFirmwaresView.swift in Sources */, 39FF05F62859850F00A86670 /* SettingsFirmwaresView.swift in Sources */,
3935F4A6286AD3E100760AB0 /* ActivityHeaderView.swift in Sources */, 3935F4A6286AD3E100760AB0 /* ActivityHeaderView.swift in Sources */,
3935F480286551FB00760AB0 /* Double+Extension.swift in Sources */, 3935F480286551FB00760AB0 /* Double+Extension.swift in Sources */,
398BE6B52B62450500FE0C29 /* FloatingAlert.swift in Sources */,
39252ABD285C8FFC00956C74 /* ListRowInstaller.swift in Sources */, 39252ABD285C8FFC00956C74 /* ListRowInstaller.swift in Sources */,
3935F49D286ABE4D00760AB0 /* FooterView.swift in Sources */, 3935F49D286ABE4D00760AB0 /* FooterView.swift in Sources */,
390451CC2856F23100E0B563 /* ScaledSystemImage.swift in Sources */, 390451CC2856F23100E0B563 /* ScaledSystemImage.swift in Sources */,

View file

@ -0,0 +1,38 @@
//
// FloatingAlert.swift
// Mist
//
// Created by Nindi Gill on 25/1/2024.
//
import Foundation
import SwiftUI
struct FloatingAlert: View {
var image: String
var message: String
private let length: CGFloat = 200
private let imageLength: CGFloat = 160
private let cornerRadius: CGFloat = 20
var body: some View {
VStack {
Image(systemName: image)
.resizable()
.scaledToFit()
Text(message)
.font(.title)
.fontWeight(.medium)
}
.foregroundStyle(.secondary)
.padding()
.frame(width: length, height: length)
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: cornerRadius))
}
}
struct FloatingAlert_Previews: PreviewProvider {
static var previews: some View {
FloatingAlert(image: "list.bullet.clipboard.fill", message: "Copied to Clipboard")
}
}

View file

@ -21,6 +21,7 @@ struct ContentView: View {
@State private var searchString: String = "" @State private var searchString: String = ""
@State private var openPanel: NSOpenPanel = .init() @State private var openPanel: NSOpenPanel = .init()
@State private var savePanel: NSSavePanel = .init() @State private var savePanel: NSSavePanel = .init()
@State private var copiedToClipboard: Bool = false
@StateObject private var taskManager: TaskManager = .shared @StateObject private var taskManager: TaskManager = .shared
private var filteredFirmwares: [Firmware] { private var filteredFirmwares: [Firmware] {
var filteredFirmwares: [Firmware] = firmwares var filteredFirmwares: [Firmware] = firmwares
@ -74,29 +75,35 @@ struct ContentView: View {
private let height: CGFloat = 720 private let height: CGFloat = 720
var body: some View { var body: some View {
// swiftlint:disable:next closure_body_length
VStack(spacing: 0) { VStack(spacing: 0) {
HeaderView(downloadType: $downloadType) HeaderView(downloadType: $downloadType)
Divider() Divider()
if downloadType == .firmware && filteredFirmwares.isEmpty || downloadType == .installer && filteredInstallers.isEmpty { if downloadType == .firmware && filteredFirmwares.isEmpty || downloadType == .installer && filteredInstallers.isEmpty {
EmptyCollectionView("No macOS \(downloadType.description)s found!\n\nಥ_ಥ") EmptyCollectionView("No macOS \(downloadType.description)s found!\n\nಥ_ಥ")
} else { } else {
List { ZStack {
ForEach(releaseNames(for: downloadType), id: \.self) { releaseName in List {
Section(header: Text(releaseName)) { ForEach(releaseNames(for: downloadType), id: \.self) { releaseName in
switch downloadType { Section(header: Text(releaseName)) {
case .firmware: switch downloadType {
ForEach(filteredFirmwares(for: releaseName)) { firmware in case .firmware:
ListRowFirmware(firmware: firmware, savePanel: $savePanel, tasksInProgress: $tasksInProgress, taskManager: taskManager) ForEach(filteredFirmwares(for: releaseName)) { firmware in
.tag(firmware) ListRowFirmware(firmware: firmware, savePanel: $savePanel, copiedToClipboard: $copiedToClipboard, tasksInProgress: $tasksInProgress, taskManager: taskManager)
} .tag(firmware)
case .installer: }
ForEach(filteredInstallers(for: releaseName)) { installer in case .installer:
ListRowInstaller(installer: installer, openPanel: $openPanel, tasksInProgress: $tasksInProgress, taskManager: taskManager) ForEach(filteredInstallers(for: releaseName)) { installer in
.tag(installer) ListRowInstaller(installer: installer, openPanel: $openPanel, tasksInProgress: $tasksInProgress, taskManager: taskManager)
.tag(installer)
}
} }
} }
} }
} }
if copiedToClipboard {
FloatingAlert(image: "list.bullet.clipboard.fill", message: "Copied to Clipboard")
}
} }
} }
Divider() Divider()
@ -119,6 +126,18 @@ struct ContentView: View {
.onAppear { .onAppear {
refresh() refresh()
} }
.onChange(of: copiedToClipboard) { copied in
guard copied else {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
withAnimation {
copiedToClipboard = false
}
}
}
} }
private func refresh() { private func refresh() {

View file

@ -17,6 +17,7 @@ struct ListRowFirmware: View {
private var retryDelay: Int = 30 private var retryDelay: Int = 30
var firmware: Firmware var firmware: Firmware
@Binding var savePanel: NSSavePanel @Binding var savePanel: NSSavePanel
@Binding var copiedToClipboard: Bool
@Binding var tasksInProgress: Bool @Binding var tasksInProgress: Bool
@ObservedObject var taskManager: TaskManager @ObservedObject var taskManager: TaskManager
@State private var alertType: FirmwareAlertType = .compatibility @State private var alertType: FirmwareAlertType = .compatibility
@ -54,14 +55,23 @@ struct ListRowFirmware: View {
size: firmware.size.bytesString(), size: firmware.size.bytesString(),
tooltip: firmware.tooltip tooltip: firmware.tooltip
) )
Button { HStack(spacing: 1) {
firmware.compatible ? validate() : showCompatibilityWarning() Button {
} label: { firmware.compatible ? validate() : showCompatibilityWarning()
Image(systemName: "arrow.down.circle") } label: {
.font(.body.bold()) Image(systemName: "arrow.down.circle")
.padding(.vertical, 1.5)
}
.help("Download macOS Firmware")
.buttonStyle(.mistAction)
Button {
copyToClipboard()
} label: {
Image(systemName: "list.bullet.clipboard")
}
.help("Copy macOS Firmware URL to Clipboard")
.buttonStyle(.mistAction)
} }
.help("Download macOS Firmware")
.buttonStyle(.mistAction)
.clipShape(Capsule()) .clipShape(Capsule())
} }
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -108,6 +118,14 @@ struct ListRowFirmware: View {
} }
} }
private func copyToClipboard() {
NSPasteboard.general.declareTypes([.string], owner: nil)
NSPasteboard.general.setString(firmware.url, forType: .string)
withAnimation(.easeIn) {
copiedToClipboard = true
}
}
private func save() { private func save() {
showSavePanel = false showSavePanel = false
savePanel.title = "Download Firmware" savePanel.title = "Download Firmware"
@ -157,6 +175,6 @@ struct ListRowFirmware: View {
struct ListRowFirmware_Previews: PreviewProvider { struct ListRowFirmware_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ListRowFirmware(firmware: .example, savePanel: .constant(NSSavePanel()), tasksInProgress: .constant(false), taskManager: .shared) ListRowFirmware(firmware: .example, savePanel: .constant(NSSavePanel()), copiedToClipboard: .constant(false), tasksInProgress: .constant(false), taskManager: .shared)
} }
} }

View file

@ -88,7 +88,7 @@ struct ListRowInstaller: View {
Button { Button {
pressButton(.download) pressButton(.download)
} label: { } label: {
Image(systemName: "arrow.down.circle").font(.body.bold()) Image(systemName: "arrow.down.circle")
} }
.help("Download and export macOS Installer") .help("Download and export macOS Installer")
.buttonStyle(.mistAction) .buttonStyle(.mistAction)
@ -98,7 +98,7 @@ struct ListRowInstaller: View {
Button { Button {
pressButton(.volumeSelection) pressButton(.volumeSelection)
} label: { } label: {
Image(systemName: "externaldrive").font(.body.bold()) Image(systemName: "externaldrive")
.padding(.vertical, 1) .padding(.vertical, 1)
} }
.help("Create bootable macOS Installer") .help("Create bootable macOS Installer")