summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipContainerView.swift
blob: ad0de9a3cfbbaac3b28d2ccbe156741ac57165aa (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
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
120
121
122
123
124
125
126
127
128
129
130
131
132
//
//  ChipContainerView.swift
//  MullvadVPN
//
//  Created by Mojgan on 2024-12-05.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import SwiftUI

struct ChipContainerView<ViewModel>: View where ViewModel: ChipViewModelProtocol {
    @ObservedObject var viewModel: ViewModel
    let tunnelState: TunnelState
    @Binding var isExpanded: Bool

    @State private var chipContainerHeight: CGFloat = .zero
    private let verticalPadding: CGFloat = 8

    var body: some View {
        GeometryReader { geo in
            let containerWidth = geo.size.width

            let (chipsToAdd, showMoreButton) =
                if isExpanded {
                    (viewModel.chips, false)
                } else {
                    viewModel.chipsToAdd(forContainerWidth: containerWidth)
                }

            HStack {
                ZStack(alignment: .topLeading) {
                    createChipViews(chips: chipsToAdd, containerWidth: containerWidth)
                }

                Button(LocalizedStringKey("\(viewModel.chips.count - chipsToAdd.count) more...")) {
                    withAnimation {
                        isExpanded.toggle()
                    }
                }
                .font(.subheadline)
                .lineLimit(1)
                .foregroundStyle(UIColor.primaryTextColor.color)
                .showIf(showMoreButton)
                .transition(.move(edge: .bottom).combined(with: .opacity))

                Spacer()
            }
            .sizeOfView { size in
                withAnimation {
                    chipContainerHeight = size.height
                }
            }
        }
        .frame(height: chipContainerHeight)
    }

    private func createChipViews(chips: [ChipModel], containerWidth: CGFloat) -> some View {
        nonisolated(unsafe) var width = CGFloat.zero
        nonisolated(unsafe) var height = CGFloat.zero

        return ForEach(chips) { data in
            ChipView(item: data) {
                viewModel.onPressed(item: data)
            }
            .padding(
                EdgeInsets(
                    top: verticalPadding,
                    leading: 0,
                    bottom: verticalPadding,
                    trailing: UIMetrics.FeatureIndicators.chipViewTrailingMargin
                )
            )
            .alignmentGuide(.leading) { dimension in
                if abs(width - dimension.width) > containerWidth {
                    width = 0
                    height -= dimension.height
                }
                let result = width
                if data.id == chips.last?.id {
                    width = 0
                } else {
                    width -= dimension.width
                }
                return result
            }
            .alignmentGuide(.top) { _ in
                let result = height
                if data.id == chips.last?.id {
                    height = 0
                }
                return result
            }
        }
    }
}

#Preview("Tap to expand") {
    StatefulPreviewWrapper(false) { isExpanded in
        ChipContainerView(
            viewModel: MockFeatureIndicatorsViewModel(),
            tunnelState: .connected(
                .init(
                    entry: nil,
                    exit: .init(
                        endpoint: .init(
                            ipv4Relay: .init(ip: .allHostsGroup, port: 1234),
                            ipv4Gateway: .allHostsGroup,
                            ipv6Gateway: .broadcast,
                            publicKey: Data()
                        ),
                        hostname: "hostname",
                        location: .init(
                            country: "Sweden",
                            countryCode: "SE",
                            city: "Gothenburg",
                            cityCode: "gbg",
                            latitude: 1234,
                            longitude: 1234
                        ),
                        features: nil
                    ),
                    retryAttempt: 0,
                    obfuscation: .off
                ),
                isPostQuantum: false,
                isDaita: false
            ),
            isExpanded: isExpanded
        )
        .background(UIColor.secondaryColor.color)
    }
}