import SwiftUI /// Browsers signed in to the web as you, with a way to end one or all. struct WebSessionsView: View { @State private var model: WebSessionsViewModel @State private var revoking: WebSession? @State private var revokingAll = false init(client: GitbayClient) { _model = State(initialValue: WebSessionsViewModel(client: client)) } var body: some View { List { if let error = model.actionError { Section { GBNotice(error, .gbWarn) } } Section { ForEach(model.state.value ?? []) { session in VStack(alignment: .leading, spacing: 3) { Text("Used \(session.lastUsedAt.formatted(.relative(presentation: .named)))") .font(.gbSans(.subheadline)) Text("Signed in \(session.createdAt.formatted(date: .abbreviated, time: .shortened)) ยท signs out \(session.expiresAt.formatted(.relative(presentation: .named))) if unused") .font(.gbSans(.caption2)) .foregroundStyle(.secondary) Text(session.id) .font(.gbMono(.caption2)) .foregroundStyle(.secondary) } .padding(.vertical, 2) .accessibilityElement(children: .combine) .swipeActions { Button("Sign Out", role: .destructive) { revoking = session } .disabled(model.working) } } } footer: { if model.state.value != nil { Text("Each is a browser signed in to the web. This app uses its token and stays signed in.") } } if let sessions = model.state.value, sessions.count > 1 { Section { Button("Sign Out Everywhere", role: .destructive) { revokingAll = true } .disabled(model.working) .accessibilityIdentifier("sessions-revoke-all") } } } .overlay { LoadStateOverlay(state: model.state) } .navigationTitle("Browser Sessions") .navigationBarTitleDisplayMode(.inline) .confirmationDialog( "Sign this browser out? It will need to sign in again.", isPresented: Binding(get: { revoking != nil }, set: { if !$0 { revoking = nil } }), titleVisibility: .visible ) { Button("Sign Out", role: .destructive) { if let session = revoking { Task { await model.revoke(id: session.id) } } revoking = nil } Button("Cancel", role: .cancel) {} } .confirmationDialog( "Sign out every browser? Each will need to sign in again.", isPresented: $revokingAll, titleVisibility: .visible ) { Button("Sign Out Everywhere", role: .destructive) { Task { await model.revokeAll() } } Button("Cancel", role: .cancel) {} } .task { await model.load() } .refreshable { await model.load() } } }