Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions apps/ios/ADE/Views/Components/ADEDesignSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -464,20 +464,31 @@ struct ADEConnectionDot: View {

private var tint: Color {
switch syncService.connectionState {
case .connected, .syncing: return ADEColor.success
case .connected: return ADEColor.success
case .syncing: return ADEColor.warning
case .connecting: return ADEColor.warning
case .error: return ADEColor.danger
case .disconnected: return ADEColor.textMuted
case .error, .disconnected: return ADEColor.danger
}
}

private var showsHostName: Bool {
private var statusText: String {
switch syncService.connectionState {
case .connected, .syncing: return true
default: return false
case .connected: return "Connected"
case .syncing: return "Syncing"
case .connecting: return "Connecting"
case .error: return "Error"
case .disconnected: return "Disconnected"
}
}

private var showsHostSuffix: Bool {
syncService.connectionState == .connected
}

private var showsConnectedGlow: Bool {
syncService.connectionState == .connected
}

private var truncatedHostName: String? {
guard let rawName = syncService.hostName else { return nil }
let cleaned = rawName
Expand All @@ -490,15 +501,28 @@ struct ADEConnectionDot: View {
}

private var accessibilityLabel: String {
let errorSuffix: String = {
guard syncService.connectionState == .error, let err = syncService.lastError?.trimmingCharacters(in: .whitespacesAndNewlines), !err.isEmpty else {
return ""
}
let clipped = err.count > 120 ? String(err.prefix(117)) + "…" : err
return ". \(clipped)"
}()
Comment thread
coderabbitai[bot] marked this conversation as resolved.

switch syncService.connectionState {
case .connected, .syncing:
case .connected:
if let name = syncService.hostName, !name.isEmpty {
return "Connected to \(name). Tap to open settings."
return "Connected to \(name)"
}
return "Connected. Tap to open settings."
case .connecting: return "Connecting. Tap to open settings."
case .error: return "Connection error. Tap to open settings."
case .disconnected: return "Not connected. Tap to open settings."
return "Connected"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
case .syncing:
return "Syncing with host"
case .connecting:
return "Connecting to host"
case .error:
return "Connection error\(errorSuffix)"
case .disconnected:
return "Disconnected from host"
}
}

Expand All @@ -510,17 +534,31 @@ struct ADEConnectionDot: View {
Circle()
.fill(tint)
.frame(width: 10, height: 10)
.shadow(color: tint.opacity(showsHostName ? 0.5 : 0), radius: showsHostName ? 4 : 0)
if showsHostName, let name = truncatedHostName {
.shadow(color: tint.opacity(showsConnectedGlow ? 0.5 : 0), radius: showsConnectedGlow ? 4 : 0)
Text(statusText)
.font(.caption.weight(.semibold))
.foregroundStyle(ADEColor.textPrimary)
.lineLimit(1)
.minimumScaleFactor(0.75)
if showsHostSuffix, let name = truncatedHostName {
Text("·")
.font(.caption.weight(.medium))
.foregroundStyle(ADEColor.textMuted)
.minimumScaleFactor(0.75)
Text(name)
.font(.caption.weight(.medium))
.foregroundStyle(ADEColor.textSecondary)
.lineLimit(1)
.minimumScaleFactor(0.75)
}
}
.frame(minHeight: 44)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.accessibilityLabel(accessibilityLabel)
.accessibilityHint("Opens settings to pair or reconnect.")
.accessibilityShowsLargeContentViewer()
}
}

Expand Down
20 changes: 0 additions & 20 deletions apps/ios/ADE/Views/Files/FilesDetailScreen+Actions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,4 @@ extension FilesDetailScreen {
hasLoadedDiff = true
}

var disconnectedNotice: ADENoticeCard {
ADENoticeCard(
title: "Read-only while disconnected",
message: needsRepairing
? "Pair again before trusting cached file previews, metadata, history, or diffs."
: "The last-loaded file preview, metadata, history, and diff stay visible, but refresh waits for the host to reconnect.",
icon: "icloud.slash",
tint: ADEColor.warning,
actionTitle: syncService.activeHostProfile == nil ? "Open Settings" : "Reconnect",
action: {
if syncService.activeHostProfile == nil {
syncService.settingsPresented = true
} else {
Task {
await syncService.reconnectIfPossible(userInitiated: true)
}
}
}
)
}
}
32 changes: 10 additions & 22 deletions apps/ios/ADE/Views/Files/FilesDetailScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ struct FilesDetailScreen: View {
let relativePath: String
let focusLine: Int?
let isFilesLive: Bool
let needsRepairing: Bool
let transitionNamespace: Namespace.ID?
let navigateToDirectory: (String) -> Void

Expand Down Expand Up @@ -94,12 +93,17 @@ struct FilesDetailScreen: View {
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
dismiss()
} label: {
Image(systemName: "chevron.left")
HStack(spacing: 10) {
Button {
dismiss()
} label: {
Image(systemName: "chevron.left")
}
.accessibilityLabel("Back")
.frame(minWidth: 44, minHeight: 44)
.contentShape(Rectangle())
ADEConnectionDot()
}
.accessibilityLabel("Back")
}
ToolbarItem(placement: .topBarTrailing) {
Button {
Expand Down Expand Up @@ -157,22 +161,6 @@ struct FilesDetailScreen: View {
}
)

if !isFilesLive {
FilesCompactBanner(
symbol: "icloud.slash",
tint: ADEColor.warning,
title: needsRepairing ? "Pair again to trust this cached view." : "Disconnected — showing last-loaded content.",
actionTitle: syncService.activeHostProfile == nil ? "Settings" : "Reconnect",
onAction: {
if syncService.activeHostProfile == nil {
syncService.settingsPresented = true
} else {
Task { await syncService.reconnectIfPossible(userInitiated: true) }
}
}
)
}

if let errorMessage {
FilesCompactBanner(
symbol: "exclamationmark.triangle.fill",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,6 @@ extension FilesDirectoryContentsView {
return (workspace.rootPath as NSString).appendingPathComponent(relativePath)
}

var disconnectedNotice: ADENoticeCard {
ADENoticeCard(
title: nodes.isEmpty ? "Reconnect to load this folder" : "Showing cached directory",
message: needsRepairing
? "The previous host trust was cleared. Pair again before trusting refreshed file state."
: "Cached rows stay browseable, but refresh and search wait for the host to reconnect.",
icon: "icloud.slash",
tint: ADEColor.warning,
actionTitle: syncService.activeHostProfile == nil ? "Open Settings" : "Reconnect",
action: {
if syncService.activeHostProfile == nil {
syncService.settingsPresented = true
} else {
Task {
await syncService.reconnectIfPossible(userInitiated: true)
}
}
}
)
}

struct DirectoryReloadKey: Hashable {
let workspaceId: String
let parentPath: String
Expand Down
6 changes: 0 additions & 6 deletions apps/ios/ADE/Views/Files/FilesDirectoryContentsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ struct FilesDirectoryContentsView: View {
let showHidden: Bool
let isLive: Bool
let isTabActive: Bool
let needsRepairing: Bool
let showDisconnectedNotice: Bool
let openDirectory: (String) -> Void
let openFile: (String, Int?) -> Void
let transitionNamespace: Namespace.ID?
Expand All @@ -21,10 +19,6 @@ struct FilesDirectoryContentsView: View {

var body: some View {
LazyVStack(alignment: .leading, spacing: 12) {
if showDisconnectedNotice && !isLive {
disconnectedNotice
}

if let errorMessage {
ADENoticeCard(
title: "Directory load failed",
Expand Down
6 changes: 3 additions & 3 deletions apps/ios/ADE/Views/Files/FilesDirectoryScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ struct FilesDirectoryScreen: View {
@Binding var showHidden: Bool
let isLive: Bool
let isTabActive: Bool
let needsRepairing: Bool
let openDirectory: (String) -> Void
let openFile: (String, Int?) -> Void
let transitionNamespace: Namespace.ID?
Expand All @@ -31,8 +30,6 @@ struct FilesDirectoryScreen: View {
showHidden: showHidden,
isLive: isLive,
isTabActive: isTabActive,
needsRepairing: needsRepairing,
showDisconnectedNotice: true,
openDirectory: openDirectory,
openFile: openFile,
transitionNamespace: transitionNamespace,
Expand All @@ -48,6 +45,9 @@ struct FilesDirectoryScreen: View {
.adeNavigationGlass()
.navigationTitle(parentPath.isEmpty ? "Root" : lastPathComponent(parentPath))
.toolbar {
ToolbarItem(placement: .topBarLeading) {
ADEConnectionDot()
}
ToolbarItemGroup(placement: .topBarTrailing) {
Button {
showHidden.toggle()
Expand Down
56 changes: 0 additions & 56 deletions apps/ios/ADE/Views/Files/FilesModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ enum FilesSearchKind {
case textSearch
}

struct FilesBrowserStatusPresentation: Equatable {
let title: String
let message: String
let actionTitle: String?
}

struct FilesBreadcrumbItem: Equatable {
let label: String
let path: String
Expand Down Expand Up @@ -164,56 +158,6 @@ func resolveFilesWorkspace(for request: FilesNavigationRequest, in workspaces: [
return nil
}

func filesBrowserStatusPresentation(
status: SyncDomainStatus,
hasCachedWorkspaces: Bool,
hasActiveHostProfile: Bool,
needsRepairing: Bool
) -> FilesBrowserStatusPresentation? {
switch status.phase {
case .disconnected:
if hasCachedWorkspaces {
return FilesBrowserStatusPresentation(
title: "Showing cached workspaces",
message: needsRepairing
? "Workspace metadata and cached directory snapshots are still visible, but you need to pair again before trusting the host or refreshing Files."
: "Workspace metadata and cached directory snapshots stay visible on iPhone, but quick open, text search, and refresh need the host to reconnect.",
actionTitle: hasActiveHostProfile ? "Reconnect" : "Open Settings"
)
}

return FilesBrowserStatusPresentation(
title: "Host disconnected",
message: hasActiveHostProfile
? "Reconnect to hydrate workspace roots, browse live directories, and run quick open or text search from Files."
: (needsRepairing
? "The previous pairing was cleared. Open Settings to pair again before Files can trust or hydrate workspace data."
: "Pair with a host from Settings to hydrate workspace roots before browsing files on iPhone."),
actionTitle: hasActiveHostProfile ? "Reconnect" : "Open Settings"
)
case .hydrating:
return FilesBrowserStatusPresentation(
title: "Hydrating workspaces",
message: "Files uses lane hydration for workspace roots. Waiting for the latest host lane data before browsing continues.",
actionTitle: nil
)
case .syncingInitialData:
return FilesBrowserStatusPresentation(
title: "Syncing initial data",
message: "Waiting for the host to finish syncing project and lane metadata before Files loads the workspace browser.",
actionTitle: nil
)
case .failed:
return FilesBrowserStatusPresentation(
title: "Workspace hydration failed",
message: status.lastError ?? "The lane graph did not hydrate, so Files cannot trust its workspace model yet.",
actionTitle: "Retry"
)
case .ready:
return nil
}
}

func filesSearchEmptyMessage(kind: FilesSearchKind, isLive: Bool, needsRepairing: Bool, query: String) -> String {
let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
let label: String = switch kind {
Expand Down
Loading
Loading