aboutsummaryrefslogtreecommitdiff
path: root/Hotline/macOS/TrackerView.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Hotline/macOS/TrackerView.swift')
-rw-r--r--Hotline/macOS/TrackerView.swift498
1 files changed, 0 insertions, 498 deletions
diff --git a/Hotline/macOS/TrackerView.swift b/Hotline/macOS/TrackerView.swift
deleted file mode 100644
index 0943d63..0000000
--- a/Hotline/macOS/TrackerView.swift
+++ /dev/null
@@ -1,498 +0,0 @@
-import Foundation
-import SwiftData
-import SwiftUI
-import UniformTypeIdentifiers
-
-struct TrackerView: View {
- @Environment(\.colorScheme) private var colorScheme
- @Environment(\.openWindow) private var openWindow
- @Environment(\.controlActiveState) private var controlActiveState
- @Environment(\.modelContext) private var modelContext
-
- @State private var refreshing = false
- @State private var trackerSheetPresented: Bool = false
- @State private var trackerSheetBookmark: Bookmark? = nil
- @State private var attemptedPrepopulate: Bool = false
- @State private var fileDropActive = false
- @State private var bookmarkExportActive = false
- @State private var bookmarkExport: BookmarkDocument? = nil
-
- @Query(sort: \Bookmark.order) private var bookmarks: [Bookmark]
- @Binding var selection: Bookmark?
-
- var body: some View {
- List(selection: $selection) {
- ForEach(bookmarks, id: \.self) { bookmark in
- TrackerItemView(bookmark: bookmark)
- .onAppear {
- Task {
- if bookmark.autoconnect, let server = bookmark.server {
- openWindow(id: "server", value: server)
- }
- }
- }
- .tag(bookmark)
-
- if bookmark.type == .tracker && bookmark.expanded {
- ForEach(bookmark.servers, id: \.self) { trackedServer in
- TrackerItemView(bookmark: trackedServer)
- .moveDisabled(true)
- .deleteDisabled(true)
- .tag(trackedServer)
- }
- }
- }
- .onMove { movedIndexes, destinationIndex in
- Bookmark.move(movedIndexes, to: destinationIndex, context: modelContext)
- }
- .onDelete { deletedIndexes in
- Bookmark.delete(at: deletedIndexes, context: modelContext)
- }
- }
- .onDeleteCommand {
- if let bookmark = selection,
- bookmark.type != .temporary
- {
- Bookmark.delete(bookmark, context: modelContext)
- }
- }
- .environment(\.defaultMinListRowHeight, 34)
- .listStyle(.inset)
- .alternatingRowBackgrounds(.enabled)
- .onChange(of: ApplicationState.shared.cloudKitReady) {
- if attemptedPrepopulate {
- print("Tracker: Already attempted to prepopulate bookmarks")
- return
- }
-
- print("Tracker: Prepopulating bookmarks")
-
- attemptedPrepopulate = true
-
- // Make sure default bookmarks are there when empty.
- Bookmark.populateDefaults(context: modelContext)
- }
- .onAppear {
- // Bookmark.deleteAll(context: modelContext)
- }
- .contextMenu(forSelectionType: Bookmark.self) { items in
- if let item = items.first {
- if item.type == .temporary {
- Button {
- let newBookmark = Bookmark(
- type: .server, name: item.name, address: item.address, port: item.port,
- login: item.login, password: item.password)
- Bookmark.add(newBookmark, context: modelContext)
- } label: {
- Label("Bookmark", systemImage: "bookmark")
- }
-
- Divider()
- }
-
- Button {
- NSPasteboard.general.clearContents()
- NSPasteboard.general.setString(item.displayAddress, forType: .string)
- } label: {
- Label("Copy Address", systemImage: "doc.on.doc")
- }
-
- if item.type == .tracker || item.type == .server {
- Divider()
-
- if item.type == .tracker {
- Button {
- trackerSheetBookmark = item
- } label: {
- Label("Edit Tracker...", systemImage: "pencil")
- }
- }
-
- if item.type == .server {
- Button {
- bookmarkExport = BookmarkDocument(bookmark: item)
- bookmarkExportActive = true
- } label: {
- Label("Export Bookmark...", systemImage: "bookmark.square")
- }
- }
-
- Divider()
-
- Button {
- Bookmark.delete(item, context: modelContext)
- } label: {
- Label(
- item.type == .tracker ? "Delete Tracker" : "Delete Bookmark", systemImage: "trash")
- }
- }
- }
- } primaryAction: { items in
- guard let clickedItem = items.first else {
- return
- }
-
- if clickedItem.type == .tracker {
- if NSEvent.modifierFlags.contains(.option) {
- trackerSheetBookmark = clickedItem
- } else {
- clickedItem.expanded.toggle()
- }
- } else if let server = clickedItem.server {
- openWindow(id: "server", value: server)
- }
- }
- .fileExporter(
- isPresented: $bookmarkExportActive, document: bookmarkExport, contentTypes: [.data],
- defaultFilename: "\(bookmarkExport?.bookmark.name ?? "Hotline Bookmark").hlbm",
- onCompletion: { result in
- switch result {
- case .success(let fileURL):
- print("Hotline Bookmark: Successfully exported:", fileURL)
- case .failure(let err):
- print("Hotline Bookmark: Failed to export:", err)
- }
-
- bookmarkExport = nil
- bookmarkExportActive = false
- }, onCancellation: {}
- )
- .onKeyPress(.rightArrow) {
- if let bookmark = selection,
- bookmark.type == .tracker
- {
- bookmark.expanded = true
- return .handled
- }
- return .ignored
- }
- .onKeyPress(.leftArrow) {
- if let bookmark = selection,
- bookmark.type == .tracker
- {
- bookmark.expanded = false
- return .handled
- }
- return .ignored
- }
- .onDrop(of: [UTType.fileURL], isTargeted: $fileDropActive) { providers, dropPoint in
- for provider in providers {
- let _ = provider.loadDataRepresentation(for: UTType.fileURL) { dataRepresentation, err in
- // HOTLINE CREATOR CODE: 1213484099
- // HOTLINE BOOKMARK TYPE CODE: 1213489773
-
- if let filePathData = dataRepresentation,
- let filePath = String(data: filePathData, encoding: .utf8),
- let fileURL = URL(string: filePath)
- {
-
- print("Hotline Bookmark: Dropped from ", fileURL.path(percentEncoded: false))
-
- DispatchQueue.main.async {
- if let newBookmark = Bookmark(fileURL: fileURL) {
- print("Hotline Bookmark: Added bookmark.")
- Bookmark.add(newBookmark, context: modelContext)
- } else {
- print("Hotline Bookmark: Failed to parse.")
- }
- }
- }
- }
- }
-
- return true
- }
- .sheet(item: $trackerSheetBookmark) { item in
- TrackerBookmarkSheet(item)
- }
- .sheet(isPresented: $trackerSheetPresented) {
- TrackerBookmarkSheet()
- }
- .navigationTitle("Servers")
- .toolbar {
- ToolbarItem(placement: .navigation) {
- Image("Hotline")
- .resizable()
- .renderingMode(.template)
- .scaledToFit()
- .foregroundColor(Color(hex: 0xE10000))
- .frame(width: 9)
- .opacity(controlActiveState == .inactive ? 0.5 : 1.0)
- }
-
- ToolbarItem(placement: .primaryAction) {
- Button {
- refreshing = true
- refresh()
- refreshing = false
- } label: {
- Label("Refresh", systemImage: "arrow.clockwise")
- }
- .disabled(refreshing)
- .help("Refresh Trackers")
- }
-
- ToolbarItem(placement: .primaryAction) {
- Button {
- trackerSheetPresented = true
- } label: {
- Label("Add Tracker", systemImage: "point.3.filled.connected.trianglepath.dotted")
- }
- .help("Add Tracker")
- }
-
- ToolbarItem(placement: .primaryAction) {
- Button {
- openWindow(id: "server")
- } label: {
- Label("Connect to Server", systemImage: "globe.americas.fill")
- }
- .help("Connect to Server")
- }
- }
- .onOpenURL(perform: { url in
- if let s = Server(url: url) {
- openWindow(id: "server", value: s)
- }
- })
- }
-
- func refresh() {
- // When a tracker is selected, refresh only that tracker.
- if let selectedBookmark = selection,
- selectedBookmark.type == .tracker
- {
- if !selectedBookmark.expanded {
- selectedBookmark.expanded = true
- } else {
- Task {
- await selectedBookmark.fetchServers()
- }
- }
- return
- }
-
- // Otherwise refresh/expand all trackers.
- for bookmark in self.bookmarks {
- if bookmark.type == .tracker {
- if !bookmark.expanded {
- bookmark.expanded = true
- } else {
- Task {
- await bookmark.fetchServers()
- }
- }
- }
- }
- }
-}
-
-struct TrackerBookmarkSheet: View {
- @Environment(\.dismiss) private var dismiss
- @Environment(\.modelContext) private var modelContext
-
- @State private var bookmark: Bookmark? = nil
- @State private var trackerAddress: String = ""
- @State private var trackerName: String = ""
-
- init() {
-
- }
-
- init(_ editingBookmark: Bookmark) {
- _bookmark = .init(initialValue: editingBookmark)
- _trackerAddress = .init(initialValue: editingBookmark.displayAddress)
- _trackerName = .init(initialValue: editingBookmark.name)
- }
-
- var body: some View {
- VStack(alignment: .leading) {
- Text("Type the address and name of a Hotline Tracker:")
- .foregroundStyle(.secondary)
- .padding(.bottom, 8)
- Form {
- Group {
- TextField(text: $trackerAddress) {
- Text("Address:")
- }
- TextField(text: $trackerName, prompt: Text("Optional")) {
- Text("Name:")
- }
- }
- .textFieldStyle(.roundedBorder)
- .controlSize(.large)
- }
- }
- .frame(width: 300)
- .fixedSize(horizontal: true, vertical: true)
- .padding()
- .toolbar {
- ToolbarItem(placement: .confirmationAction) {
- Button(self.bookmark != nil ? "Save Tracker" : "Add Tracker") {
- var displayName = trackerName.trimmingCharacters(in: .whitespacesAndNewlines)
- let (host, port) = Tracker.parseTrackerAddressAndPort(trackerAddress)
-
- if displayName.isEmpty {
- displayName = host
- }
-
- if !displayName.isEmpty && !host.isEmpty {
- if !host.isEmpty {
- if self.bookmark != nil {
- // We're editing an existing bookmark.
- self.bookmark?.name = displayName
- self.bookmark?.address = host
- self.bookmark?.port = port
- } else {
- // We're creating a new bookmark.
- let newBookmark = Bookmark(
- type: .tracker, name: displayName, address: host, port: port)
- Bookmark.add(newBookmark, context: modelContext)
- }
-
- self.trackerName = ""
- self.trackerAddress = ""
-
- dismiss()
- }
- }
- }
- }
- ToolbarItem(placement: .cancellationAction) {
- Button("Cancel") {
- self.trackerName = ""
- self.trackerAddress = ""
-
- dismiss()
- }
- }
- }
- }
-}
-
-struct TrackerItemView: View {
- let bookmark: Bookmark
-
- @State private var onlineAnimationMaxState: Bool = true
-
- var body: some View {
- HStack(alignment: .center, spacing: 6) {
- if bookmark.type == .tracker {
- Button {
- bookmark.expanded.toggle()
- } label: {
- Text(Image(systemName: bookmark.expanded ? "chevron.down" : "chevron.right"))
- .bold()
- .font(.system(size: 10))
- .opacity(0.5)
- .frame(alignment: .center)
- }
- .buttonStyle(.plain)
- .frame(width: 10)
- .padding(.leading, 4)
- .padding(.trailing, 2)
- }
-
- switch bookmark.type {
- case .tracker:
- Image("Tracker")
- .resizable()
- .scaledToFit()
- .frame(width: 16, height: 16, alignment: .center)
- Text(bookmark.name).bold().lineLimit(1).truncationMode(.tail)
- if bookmark.loading {
- ProgressView()
- .padding([.leading, .trailing], 2)
- .controlSize(.small)
- }
- Spacer(minLength: 0)
- case .server:
- Image(systemName: "bookmark.fill")
- .resizable()
- .renderingMode(.template)
- .aspectRatio(contentMode: .fit)
- .frame(width: 11, height: 11, alignment: .center)
- .opacity(0.5)
- .padding(.leading, 3)
- Image(systemName: "bolt.fill")
- .resizable()
- .renderingMode(.template)
- .aspectRatio(contentMode: .fit)
- .frame(width: 13, height: 13, alignment: .center)
- .opacity(0.5)
- .padding(.leading, 3)
- .padding(.trailing, 2)
- .foregroundStyle(bookmark.autoconnect ? Color.accentColor : Color.gray)
- .onTapGesture {
- bookmark.autoconnect.toggle()
- try? bookmark.modelContext?.save()
- }
- Image("Server")
- .resizable()
- .scaledToFit()
- .frame(width: 16, height: 16, alignment: .center)
- Text(bookmark.name).lineLimit(1).truncationMode(.tail)
- Spacer(minLength: 0)
- case .temporary:
- Spacer()
- .frame(width: 14 + 8 + 16)
- Image("Server")
- .resizable()
- .scaledToFit()
- .frame(width: 16, height: 16, alignment: .center)
- Text(bookmark.name).lineLimit(1).truncationMode(.tail)
- if let serverDescription = bookmark.serverDescription {
- Text(serverDescription)
- .foregroundStyle(.secondary)
- .lineLimit(1)
- .truncationMode(.tail)
- }
- Spacer(minLength: 0)
- if let serverUserCount = bookmark.serverUserCount,
- serverUserCount > 0
- {
- Text(String(serverUserCount))
- .foregroundStyle(.secondary)
- .lineLimit(1)
-
- Circle()
- .fill(.fileComplete)
- .frame(width: 7, height: 7)
- .opacity(onlineAnimationMaxState ? 1.0 : 0.2)
- .onAppear {
- withAnimation(.easeInOut(duration: 1.0).repeatForever()) {
- onlineAnimationMaxState.toggle()
- }
- }
- .padding(.trailing, 6)
- }
- }
- }
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- .onChange(of: bookmark.expanded) {
- guard bookmark.type == .tracker else {
- return
- }
-
- if bookmark.expanded {
- Task {
- await bookmark.fetchServers()
- }
- }
- }
- }
-}
-
-#if DEBUG
- private struct TrackerViewPreview: View {
- @State var selection: Bookmark? = nil
-
- var body: some View {
- TrackerView(selection: $selection)
- }
- }
-
- #Preview {
- TrackerViewPreview()
- }
-#endif