Files
yattee/Yattee/Views/Settings/AddSourceView.swift
Arkadiusz Fal 86fbdd23f1 Allow importing account-less legacy instances as sources
Legacy v1 instances without an account had no import path after the
switch to explicit account review. Surface them in a separate "Sources"
section of the legacy import view where they can be added with a single
tap (no sign-in) or removed, mirroring the accounts flow. Instances tied
to an account stay in the Accounts section and are not duplicated.

Detection now gates the entry points and first-launch prompt on any
legacy data (accounts or sources), not accounts alone.
2026-05-18 23:54:42 +02:00

275 lines
9.9 KiB
Swift

//
// AddSourceView.swift
// Yattee
//
// Selection screen for adding new sources. Presents a list of source types
// with NavigationLinks to dedicated forms for each type.
//
import SwiftUI
struct AddSourceView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.appEnvironment) private var appEnvironment
// Network discovery sheet
@State private var showingNetworkDiscovery = false
@State private var selectedShareType: DiscoveredShare.ShareType?
// Navigation destinations from network discovery
@State private var navigateToWebDAV = false
@State private var navigateToSMB = false
@State private var discoveredWebDAVURL: URL?
@State private var discoveredSMBServer: String?
@State private var discoveredName: String?
@State private var discoveredAllowInvalidCerts = false
@State private var hasLegacyAccountsToImport = false
var body: some View {
#if os(tvOS)
listContent
.navigationDestination(isPresented: $navigateToWebDAV) {
TVSidebarDetailContainer(
systemImage: "externaldrive.connected.to.line.below",
title: String(localized: "sources.addWebDAV")
) {
AddWebDAVView(
prefillURL: discoveredWebDAVURL,
prefillName: discoveredName,
prefillAllowInvalidCertificates: discoveredAllowInvalidCerts
)
}
}
.navigationDestination(isPresented: $navigateToSMB) {
TVSidebarDetailContainer(
systemImage: "server.rack",
title: String(localized: "sources.addSMB")
) {
AddSMBView(
prefillServer: discoveredSMBServer,
prefillName: discoveredName
)
}
}
.sheet(isPresented: $showingNetworkDiscovery) {
NetworkShareDiscoverySheet(filterType: selectedShareType) { share in
handleSelectedShare(share)
}
}
#else
NavigationStack {
listContent
.navigationTitle(String(localized: "sources.newSource"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
}
.navigationDestination(isPresented: $navigateToWebDAV) {
AddWebDAVView(
prefillURL: discoveredWebDAVURL,
prefillName: discoveredName,
prefillAllowInvalidCertificates: discoveredAllowInvalidCerts,
dismissSheet: dismiss
)
}
.navigationDestination(isPresented: $navigateToSMB) {
AddSMBView(
prefillServer: discoveredSMBServer,
prefillName: discoveredName,
dismissSheet: dismiss
)
}
}
#if os(macOS)
.frame(minWidth: 560, minHeight: 560)
#endif
.sheet(isPresented: $showingNetworkDiscovery) {
NetworkShareDiscoverySheet(filterType: selectedShareType) { share in
handleSelectedShare(share)
}
}
#endif
}
private var listContent: some View {
List {
if hasLegacyAccountsToImport {
Section {
NavigationLink {
#if os(tvOS)
TVSidebarDetailContainer(
systemImage: "person.badge.key",
title: String(localized: "migration.accounts.title")
) {
LegacyAccountsImportView(showsDoneButton: false)
}
#else
LegacyAccountsImportView(showsDoneButton: false)
#endif
} label: {
Label(String(localized: "migration.accounts.addSourcesLink"), systemImage: "person.badge.key")
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
} footer: {
Text(String(localized: "migration.accounts.addSourcesFooter"))
}
}
Section {
#if !os(tvOS)
NavigationLink {
AddLocalFolderView(dismissSheet: dismiss)
} label: {
Label(String(localized: "sources.addLocalFolder"), systemImage: "folder")
}
#endif
NavigationLink {
#if os(tvOS)
TVSidebarDetailContainer(
systemImage: "externaldrive.connected.to.line.below",
title: String(localized: "sources.addWebDAV")
) {
AddWebDAVView()
}
#else
AddWebDAVView(dismissSheet: dismiss)
#endif
} label: {
Label(String(localized: "sources.addWebDAV"), systemImage: "externaldrive.connected.to.line.below")
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
NavigationLink {
#if os(tvOS)
TVSidebarDetailContainer(
systemImage: "server.rack",
title: String(localized: "sources.addSMB")
) {
AddSMBView()
}
#else
AddSMBView(dismissSheet: dismiss)
#endif
} label: {
Label(String(localized: "sources.addSMB"), systemImage: "server.rack")
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
NavigationLink {
#if os(tvOS)
TVSidebarDetailContainer(
systemImage: "globe",
title: String(localized: "sources.addRemoteServer")
) {
AddRemoteServerView()
}
#else
AddRemoteServerView(dismissSheet: dismiss)
#endif
} label: {
Label(String(localized: "sources.addRemoteServer"), systemImage: "globe")
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
NavigationLink {
#if os(tvOS)
TVSidebarDetailContainer(
systemImage: "globe",
title: String(localized: "sources.browsePeerTube")
) {
if let appEnvironment {
PeerTubeInstancesExploreView()
.appEnvironment(appEnvironment)
}
}
#else
if let appEnvironment {
PeerTubeInstancesExploreView()
.appEnvironment(appEnvironment)
}
#endif
} label: {
Label {
Text(String(localized: "sources.browsePeerTube"))
} icon: {
Image("peertube")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
}
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
}
Section {
Button {
selectedShareType = nil
showingNetworkDiscovery = true
} label: {
Label(String(localized: "discovery.scanNetwork"), systemImage: "wifi")
#if os(tvOS)
.labelStyle(TVSourceRowLabelStyle())
#endif
}
#if os(tvOS)
.buttonStyle(TVFormRowButtonStyle())
#endif
} footer: {
Text(String(localized: "sources.footer.discovery"))
}
}
.task {
refreshLegacyAccountsVisibility()
}
.onChange(of: appEnvironment?.legacyMigrationService.legacyDataRevision ?? 0) {
refreshLegacyAccountsVisibility()
}
}
private func handleSelectedShare(_ share: DiscoveredShare) {
discoveredName = share.name
switch share.type {
case .webdav, .webdavs:
discoveredWebDAVURL = share.url
discoveredAllowInvalidCerts = share.type == .webdavs
navigateToWebDAV = true
case .smb:
discoveredSMBServer = share.host
navigateToSMB = true
}
}
private func refreshLegacyAccountsVisibility() {
hasLegacyAccountsToImport = appEnvironment?.legacyMigrationService.hasLegacyDataToImport() == true
}
}
// MARK: - Preview
#Preview {
AddSourceView()
.appEnvironment(.preview)
}