summaryrefslogtreecommitdiff
path: root/macos/Sources/Features/Update/UpdatePill.swift
blob: b975e81c920ffa9f2682f282052da0247d3f3a11 (plain)
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
import SwiftUI

/// A pill-shaped button that displays update status and provides access to update actions.
struct UpdatePill: View {
    /// The update view model that provides the current state and information
    @ObservedObject var model: UpdateViewModel
    
    /// Whether the update popover is currently visible
    @State private var showPopover = false
    
    /// The font used for the pill text
    private let textFont = NSFont.systemFont(ofSize: 11, weight: .medium)
    
    var body: some View {
        if !model.state.isIdle {
            pillButton
                .popover(isPresented: $showPopover, arrowEdge: .bottom) {
                    UpdatePopoverView(model: model)
                }
                .transition(.opacity.combined(with: .scale(scale: 0.95)))
                .onChange(of: model.state) { newState in
                    if case .notFound = newState {
                        Task {
                            try? await Task.sleep(for: .seconds(5))
                            if case .notFound = model.state {
                                model.state = .idle
                            }
                        }
                    }
                }
        }
    }
    
    /// The pill-shaped button view that displays the update badge and text
    @ViewBuilder
    private var pillButton: some View {
        Button(action: {
            if case .notFound = model.state {
                model.state = .idle
            } else {
                showPopover.toggle()
            }
        }) {
            HStack(spacing: 6) {
                UpdateBadge(model: model)
                    .frame(width: 14, height: 14)
                
                Text(model.text)
                    .font(Font(textFont))
                    .lineLimit(1)
                    .frame(width: textWidth)
            }
            .padding(.horizontal, 8)
            .padding(.vertical, 4)
            .background(
                Capsule()
                    .fill(model.backgroundColor)
            )
            .foregroundColor(model.foregroundColor)
            .contentShape(Capsule())
        }
        .buttonStyle(.plain)
        .help(model.text)
    }
    
    /// Calculated width for the text to prevent resizing during progress updates
    private var textWidth: CGFloat? {
        let attributes: [NSAttributedString.Key: Any] = [.font: textFont]
        let size = (model.maxWidthText as NSString).size(withAttributes: attributes)
        return size.width
    }
}