diff options
| author | Christian Cleberg <[email protected]> | 2026-04-02 00:33:58 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-02 00:33:58 -0500 |
| commit | 7b4a9fd51cfaf22114daa0170675102d04625791 (patch) | |
| tree | b4331b9aef4dc3f52698ad4bf64c7554ea557d9e /Hutch/Views/Repositories | |
| parent | f820109a956c627e510880a7cac88cb35032203b (diff) | |
| download | hutch-7b4a9fd51cfaf22114daa0170675102d04625791.tar.gz hutch-7b4a9fd51cfaf22114daa0170675102d04625791.tar.bz2 hutch-7b4a9fd51cfaf22114daa0170675102d04625791.zip | |
add hg parity
Diffstat (limited to 'Hutch/Views/Repositories')
| -rw-r--r-- | Hutch/Views/Repositories/FileTreeView.swift | 6 | ||||
| -rw-r--r-- | Hutch/Views/Repositories/HgRepositoryDetailView.swift | 141 |
2 files changed, 115 insertions, 32 deletions
diff --git a/Hutch/Views/Repositories/FileTreeView.swift b/Hutch/Views/Repositories/FileTreeView.swift index adacb2e..0e7c56e 100644 --- a/Hutch/Views/Repositories/FileTreeView.swift +++ b/Hutch/Views/Repositories/FileTreeView.swift @@ -369,7 +369,7 @@ private struct FileTreeContentView: View { } } -private struct CodeFileTextView: UIViewRepresentable { +struct CodeFileTextView: UIViewRepresentable { let text: String let fileName: String let wrapLines: Bool @@ -396,7 +396,7 @@ private struct CodeFileTextView: UIViewRepresentable { } } -private struct FileContentShareSheet: UIViewControllerRepresentable { +struct FileContentShareSheet: UIViewControllerRepresentable { let activityItems: [Any] func makeUIViewController(context _: Context) -> UIActivityViewController { @@ -406,7 +406,7 @@ private struct FileContentShareSheet: UIViewControllerRepresentable { func updateUIViewController(_ : UIActivityViewController, context _: Context) {} } -private final class CodeFileUIView: UIView { +final class CodeFileUIView: UIView { private enum Layout { static let verticalPadding: CGFloat = 12 static let gutterLeadingPadding: CGFloat = 12 diff --git a/Hutch/Views/Repositories/HgRepositoryDetailView.swift b/Hutch/Views/Repositories/HgRepositoryDetailView.swift index 575ce81..71fd6a0 100644 --- a/Hutch/Views/Repositories/HgRepositoryDetailView.swift +++ b/Hutch/Views/Repositories/HgRepositoryDetailView.swift @@ -1,4 +1,5 @@ import SwiftUI +import UIKit struct HgRepositoryDetailView: View { let repository: RepositorySummary @@ -8,11 +9,16 @@ struct HgRepositoryDetailView: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme + @AppStorage(AppStorageKeys.wrapRepositoryFileLines) private var wrapRepositoryFileLines = false @State private var viewModel: HgRepositoryDetailViewModel? @State private var selectedTab: HgRepositoryDetailViewModel.Tab = .summary @State private var showSettings = false @State private var isShowingRepositoryDetails = false @State private var showBrowseRefPicker = false + @State private var showFileShareSheet = false + @State private var showShareUnavailableAlert = false + @State private var didCopyFileContents = false + @State private var copyResetTask: Task<Void, Never>? private var canManageRepository: Bool { guard let currentUser = appState.currentUser else { return false } @@ -80,6 +86,9 @@ struct HgRepositoryDetailView: View { HgBrowseRefPickerSheet(viewModel: viewModel, isPresented: $showBrowseRefPicker) } } + .onChange(of: viewModel?.selectedFilePath) { _, _ in + resetCopyConfirmation() + } .task { if viewModel == nil { let vm = HgRepositoryDetailViewModel(repository: repository, client: appState.client) @@ -266,39 +275,24 @@ struct HgRepositoryDetailView: View { SRHTLoadingStateView(message: "Loading files…") } else if let selectedFilePath = viewModel.selectedFilePath, let fileContent = viewModel.fileContent { VStack(spacing: 0) { - HStack { - Spacer() - SRHTShareButton(url: shareURL, target: .file) { - Label("Share File", systemImage: "square.and.arrow.up") - } - .buttonStyle(.bordered) - } - .padding(.horizontal) - .padding(.top, 12) - - GeometryReader { geometry in - ScrollView([.vertical, .horizontal]) { - Text(fileContent) - .font(.system(.body, design: .monospaced)) - .frame( - minWidth: geometry.size.width, - minHeight: geometry.size.height, - alignment: .topLeading - ) - .padding() - } - } + CodeFileTextView( + text: fileContent, + fileName: selectedFilePath.split(separator: "/").last.map(String.init) ?? selectedFilePath, + wrapLines: wrapRepositoryFileLines + ) } .safeAreaInset(edge: .bottom) { - Button("Back to directory") { - viewModel.dismissFileView() - } - .buttonStyle(.bordered) - .padding(.vertical, 8) - .frame(maxWidth: .infinity) - .background(.bar) + fileActionToolbar(fileContent: fileContent, viewModel: viewModel) } .navigationTitle(selectedFilePath.split(separator: "/").last.map(String.init) ?? repository.name) + .sheet(isPresented: $showFileShareSheet) { + FileContentShareSheet(activityItems: [shareURL ?? fileContent]) + } + .alert("Share Unavailable", isPresented: $showShareUnavailableAlert) { + Button("OK", role: .cancel) {} + } message: { + Text(SRHTShareTarget.file.fallbackMessage) + } } else if let error = viewModel.error, viewModel.files.isEmpty { SRHTErrorStateView( title: "Couldn't Load Files", @@ -508,6 +502,95 @@ struct HgRepositoryDetailView: View { name.hasSuffix("/") ? String(name.dropLast()) : name } + private func shareFileContents(_ text: String) { + if text.isEmpty { + showShareUnavailableAlert = true + } else { + showFileShareSheet = true + } + } + + private func copyFileContents(_ text: String) { + UIPasteboard.general.string = text + didCopyFileContents = true + copyResetTask?.cancel() + copyResetTask = Task { + try? await Task.sleep(for: .seconds(2)) + guard !Task.isCancelled else { return } + await MainActor.run { + didCopyFileContents = false + } + } + } + + private func resetCopyConfirmation() { + copyResetTask?.cancel() + copyResetTask = nil + didCopyFileContents = false + } + + private func fileActionToolbar(fileContent: String, viewModel: HgRepositoryDetailViewModel) -> some View { + HStack(spacing: 0) { + toolbarButton( + title: "Share", + systemImage: "square.and.arrow.up" + ) { + if shareURL != nil { + showFileShareSheet = true + } else { + shareFileContents(fileContent) + } + } + + toolbarButton( + title: didCopyFileContents ? "Copied" : "Copy All", + systemImage: didCopyFileContents ? "checkmark" : "doc.on.doc" + ) { + copyFileContents(fileContent) + } + + toolbarButton( + title: wrapRepositoryFileLines ? "Wrap On" : "Wrap Off", + systemImage: "text.word.spacing" + ) { + wrapRepositoryFileLines.toggle() + } + + toolbarButton( + title: "Back", + systemImage: "chevron.left" + ) { + viewModel.dismissFileView() + } + } + .padding(.horizontal, 8) + .padding(.top, 10) + .padding(.bottom, 8) + .background(.bar) + .overlay(alignment: .top) { + Divider() + } + } + + private func toolbarButton( + title: String, + systemImage: String, + action: @escaping () -> Void + ) -> some View { + Button(action: action) { + VStack(spacing: 4) { + Image(systemName: systemImage) + .font(.system(size: 17, weight: .semibold)) + Text(title) + .font(.caption2) + .lineLimit(1) + } + .frame(maxWidth: .infinity) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .foregroundStyle(.primary) + } } private struct HgBrowseRefPickerSheet: View { |
