summaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/RepositoryDetailView.swift
blob: 39ef912944a13847d62065116a5bc9e48a643c0e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import SwiftUI

struct RepositoryDetailView: View {
    var repository: RepositorySummary
    var onDeleted: (() -> Void)?

    @Environment(AppState.self) private var appState
    @Environment(\.dismiss) private var dismiss
    @State private var viewModel: RepositoryDetailViewModel?
    @State private var selectedTab: RepositoryDetailViewModel.Tab = .summary
    @State private var showSettings = false
    @State private var displayName: String

    private var canManageRepository: Bool {
        guard let currentUser = appState.currentUser else { return false }
        return normalizedUsername(currentUser.username) == normalizedUsername(repository.owner.canonicalName)
    }

    init(repository: RepositorySummary, onDeleted: (() -> Void)? = nil) {
        self.repository = repository
        self.onDeleted = onDeleted
        self._displayName = State(initialValue: repository.name)
    }

    var body: some View {
        if repository.service == .hg {
            HgRepositoryDetailView(repository: repository, onDeleted: onDeleted)
        } else {
            Group {
                if let viewModel {
                    detailContent(viewModel)
                } else {
                    SRHTLoadingStateView(message: "Loading repository…")
                }
            }
            .navigationTitle(displayName)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItemGroup(placement: .topBarTrailing) {
                    SRHTShareButton(url: SRHTWebURL.repository(repository), target: .repository) {
                        Image(systemName: "square.and.arrow.up")
                    }

                    if canManageRepository {
                        Button {
                            showSettings = true
                        } label: {
                            Image(systemName: "gear")
                        }
                    }
                }
            }
            .sheet(isPresented: $showSettings) {
                RepositorySettingsView(
                    repository: repository,
                    branches: viewModel?.branches ?? [],
                    client: appState.client,
                    onRenamed: { newName in
                        displayName = newName
                    },
                    onDeleted: {
                        dismiss()
                        onDeleted?()
                    }
                )
            }
            .task {
                if viewModel == nil {
                    viewModel = RepositoryDetailViewModel(
                        repository: repository,
                        client: appState.client
                    )
                }
            }
        }
    }

    @ViewBuilder
    private func detailContent(_ viewModel: RepositoryDetailViewModel) -> some View {
        VStack(spacing: 0) {
            Picker("Tab", selection: $selectedTab) {
                ForEach(RepositoryDetailViewModel.Tab.allCases, id: \.self) { tab in
                    Text(tab.rawValue).tag(tab)
                }
            }
            .pickerStyle(.segmented)
            .padding(.horizontal)
            .padding(.vertical, 8)

            Divider()

            switch selectedTab {
            case .summary:
                ReadmeView(viewModel: viewModel)
            case .tree:
                FileTreeView(
                    repository: repository,
                    client: appState.client
                )
            case .log:
                CommitLogView(viewModel: viewModel)
            case .refs:
                ReferencesListView(viewModel: viewModel)
            case .artifacts:
                ArtifactsView(viewModel: viewModel)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
        .srhtErrorBanner(error: Binding(
            get: { viewModel.error },
            set: { viewModel.error = $0 }
        ))
    }

    private func normalizedUsername(_ value: String) -> String {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        return trimmed.hasPrefix("~") ? String(trimmed.dropFirst()) : trimmed
    }
}