Add SwiftLint rule to enforce stack spacing. (#2080)

Xcode 15 changes the default behaviour and given we're always working from designs, we shouldn't rely on it for our layout.
This commit is contained in:
Doug 2023-11-14 12:38:38 +00:00 committed by GitHub
parent 752ad66397
commit 2c1e61a1bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 289 additions and 293 deletions

View File

@ -49,6 +49,18 @@ nesting:
warning: 5
custom_rules:
vstack_spacing:
regex: "(?-s)VStack((?!spacing:).)*\\s*\\{"
match_kinds: identifier
message: "Please use explicit spacing in VStacks."
severity: warning
hstack_spacing:
regex: "(?-s)HStack((?!spacing:).)*\\s*\\{"
match_kinds: identifier
message: "Please use explicit spacing in HStacks."
severity: warning
print_deprecation:
regex: "\\b(print)\\b"
match_kinds: identifier

View File

@ -263,7 +263,7 @@
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"location" : "https://github.com/siteline/SwiftUI-Introspect",
"state" : {
"revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290",
"version" : "0.9.2"

View File

@ -86,7 +86,7 @@ struct MapLibreStaticMapView<PinAnnotation: View>: View {
} label: {
placeholderImage
.overlay {
VStack {
VStack(spacing: 0) {
Image(systemName: "arrow.clockwise")
Text(L10n.actionStaticMapLoad)
}

View File

@ -87,7 +87,7 @@ struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
userIndicatorController: ServiceLocator.shared.userIndicatorController)
static var previews: some View {
VStack {
VStack(spacing: 0) {
ForEach(0...8, id: \.self) { _ in
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
}

View File

@ -97,7 +97,7 @@ struct FormButtonStyle: PrimitiveButtonStyle {
var accessory: FormRowAccessory?
func makeBody(configuration: Configuration) -> some View {
HStack {
HStack(spacing: 8) {
configuration.label
.labelStyle(FormRowLabelStyle(role: configuration.role))
.foregroundColor(.compound.textPrimary)
@ -126,7 +126,7 @@ struct FormActionButtonStyle: ButtonStyle {
let title: String
func makeBody(configuration: Configuration) -> some View {
VStack {
VStack(spacing: 8) {
configuration.label
.buttonStyle(.plain)
.foregroundColor(.compound.textPrimary)

View File

@ -61,7 +61,7 @@ struct FormRowLabelStyle: LabelStyle {
struct FormRowLabelStyle_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 12) {
Label("Person", systemImage: "person")
.labelStyle(FormRowLabelStyle())

View File

@ -50,7 +50,7 @@ struct FullscreenDialog<Content: View, BottomContent: View>: View {
var standardLayout: some View {
GeometryReader { geometry in
ScrollView {
VStack {
VStack(spacing: 0) {
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))
@ -62,7 +62,7 @@ struct FullscreenDialog<Content: View, BottomContent: View>: View {
}
.scrollBounceBehavior(.basedOnSize)
.safeAreaInset(edge: .bottom) {
VStack {
VStack(spacing: 0) {
bottomContent()
.readableFrame()
.padding(.horizontal, horizontalPadding)
@ -81,7 +81,7 @@ struct FullscreenDialog<Content: View, BottomContent: View>: View {
var accessibilityLayout: some View {
GeometryReader { geometry in
ScrollView {
VStack {
VStack(spacing: 0) {
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))

View File

@ -30,7 +30,7 @@ struct UserIndicatorModalView: View {
ProgressView(value: progressFraction)
}
HStack {
HStack(spacing: 8) {
if let iconName = indicator.iconName {
Image(systemName: iconName)
.font(.compound.bodyLG)

View File

@ -124,7 +124,7 @@ private struct WaveformShape: Shape {
struct EstimatedWaveformView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
// Wrap the WaveformView in a VStack otherwise the preview test will fail (because of Prefire / GeometryReader)
VStack {
VStack(spacing: 0) {
EstimatedWaveformView(waveform: EstimatedWaveform.mockWaveform, progress: 0.5)
.frame(width: 140, height: 50)
}

View File

@ -110,12 +110,12 @@ extension VoiceMessageButton.State {
struct VoiceMessageButton_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack {
HStack {
VStack(spacing: 8) {
HStack(spacing: 8) {
VoiceMessageButton(state: .paused, size: .small, action: { })
VoiceMessageButton(state: .paused, size: .medium, action: { })
}
HStack {
HStack(spacing: 8) {
VoiceMessageButton(state: .playing, size: .small, action: { })
VoiceMessageButton(state: .playing, size: .medium, action: { })
}

View File

@ -102,7 +102,7 @@ private struct PINDigitField: View {
struct PINTextField_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack {
VStack(spacing: 8) {
PreviewWrapper(pinCode: "", isSecure: false)
PreviewWrapper(pinCode: "12", isSecure: false)
PreviewWrapper(pinCode: "1234", isSecure: false)

View File

@ -123,12 +123,12 @@ struct CompletionSuggestion_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
// Putting them is VStack allows the preview to work properly in tests
VStack {
VStack(spacing: 8) {
CompletionSuggestionView(imageProvider: MockMediaProvider(),
items: [.user(item: MentionSuggestionItem(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil)),
.user(item: MentionSuggestionItem(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: URL.documentsDirectory))]) { _ in }
}
VStack {
VStack(spacing: 8) {
CompletionSuggestionView(imageProvider: MockMediaProvider(),
items: multipleItems) { _ in }
}

View File

@ -299,7 +299,7 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview {
ComposerToolbar.mock(focused: true)
// Putting them is VStack allows the completion suggestion preview to work properly in tests
VStack {
VStack(spacing: 8) {
// The mock functon can't be used in this context because it does not hold a reference to the view model, losing the combine subscriptions
ComposerToolbar(context: composerViewModel.context,
wysiwygViewModel: wysiwygViewModel,
@ -307,7 +307,7 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview {
}
.previewDisplayName("With Suggestions")
VStack {
VStack(spacing: 8) {
ComposerToolbar.textWithVoiceMessage(focused: false)
ComposerToolbar.textWithVoiceMessage(focused: true)
ComposerToolbar.voiceMessageRecordingMock(recording: true)

View File

@ -154,7 +154,7 @@ private struct MessageComposerEditHeader: View {
let action: () -> Void
var body: some View {
HStack(alignment: .center) {
HStack(alignment: .center, spacing: 8) {
Label(L10n.commonEditing,
iconAsset: Asset.Images.editing,
iconSize: .xSmall,
@ -227,7 +227,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
}
static var previews: some View {
VStack {
VStack(spacing: 8) {
messageComposer(sendingDisabled: true)
messageComposer("Some message",
@ -240,7 +240,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
.padding(.horizontal)
ScrollView {
VStack {
VStack(spacing: 8) {
ForEach(replyTypes, id: \.self) { replyDetails in
messageComposer(mode: .reply(itemID: .random,
replyDetails: replyDetails, isThread: false))
@ -252,7 +252,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
.previewDisplayName("Replying")
ScrollView {
VStack {
VStack(spacing: 8) {
ForEach(replyTypes, id: \.self) { replyDetails in
messageComposer(mode: .reply(itemID: .random,
replyDetails: replyDetails, isThread: true))

View File

@ -41,18 +41,16 @@ struct VoiceMessagePreviewComposer: View {
}
var body: some View {
HStack {
HStack {
VoiceMessageButton(state: .init(playerState.playerButtonPlaybackState),
size: .small,
action: onPlayPause)
Text(timeLabelContent)
.lineLimit(1)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textSecondary)
.monospacedDigit()
.fixedSize(horizontal: true, vertical: true)
}
HStack(spacing: 8) {
VoiceMessageButton(state: .init(playerState.playerButtonPlaybackState),
size: .small,
action: onPlayPause)
Text(timeLabelContent)
.lineLimit(1)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textSecondary)
.monospacedDigit()
.fixedSize(horizontal: true, vertical: true)
waveformView
.waveformInteraction(isDragging: $isDragging,
@ -130,9 +128,7 @@ struct VoiceMessagePreviewComposer_Previews: PreviewProvider, TestablePreview {
static let waveformData: [Float] = Array(repeating: 1.0, count: 1000)
static var previews: some View {
VStack {
VoiceMessagePreviewComposer(playerState: playerState, waveform: .data(waveformData), onPlay: { }, onPause: { }, onSeek: { _ in }, onScrubbing: { _ in })
.fixedSize(horizontal: false, vertical: true)
}
VoiceMessagePreviewComposer(playerState: playerState, waveform: .data(waveformData), onPlay: { }, onPause: { }, onSeek: { _ in }, onScrubbing: { _ in })
.fixedSize(horizontal: false, vertical: true)
}
}

View File

@ -69,7 +69,7 @@ private struct VoiceMessageRecordingButtonStyle: ButtonStyle {
struct VoiceMessageRecordingButton_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
HStack {
HStack(spacing: 8) {
VoiceMessageRecordingButton(mode: .idle)
VoiceMessageRecordingButton(mode: .recording)

View File

@ -45,9 +45,7 @@ struct VoiceMessageRecordingComposer_Previews: PreviewProvider, TestablePreview
static let recorderState = AudioRecorderState()
static var previews: some View {
VStack {
VoiceMessageRecordingComposer(recorderState: recorderState)
.fixedSize(horizontal: false, vertical: true)
}
VoiceMessageRecordingComposer(recorderState: recorderState)
.fixedSize(horizontal: false, vertical: true)
}
}

View File

@ -42,7 +42,7 @@ struct VoiceMessageRecordingView: View {
}
var body: some View {
HStack {
HStack(spacing: 8) {
VoiceMessageRecordingBadge()
.frame(width: recordingIndicatorSize, height: recordingIndicatorSize)

View File

@ -137,7 +137,7 @@ private struct CreatePollOptionView: View {
let deleteAction: () -> Void
var body: some View {
HStack {
HStack(spacing: 8) {
if editMode?.wrappedValue == .active {
Button(role: .destructive, action: deleteAction) {
CompoundIcon(\.delete)

View File

@ -20,19 +20,15 @@ struct EmojiPickerScreenHeaderView: View {
let title: String
var body: some View {
HStack {
Text(title)
.font(.compound.bodyMD.bold())
.foregroundColor(.compound.textPrimary)
.frame(maxWidth: .infinity, alignment: .leading)
}
Text(title)
.font(.compound.bodyMD.bold())
.foregroundColor(.compound.textPrimary)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct EmojiPickerScreenHeaderView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
Group {
EmojiPickerScreenHeaderView(title: "Title")
}
EmojiPickerScreenHeaderView(title: "Title")
}
}

View File

@ -103,7 +103,7 @@ struct HomeScreenRoomCell: View {
@ViewBuilder
private var footer: some View {
HStack(alignment: .firstTextBaseline) {
HStack(alignment: .firstTextBaseline, spacing: 0) {
ZStack(alignment: .topLeading) {
// Hidden text with 2 lines to maintain consistent height, scaling with dynamic text.
Text(" \n ")

View File

@ -70,7 +70,7 @@ struct InvitesScreenCell: View {
@ViewBuilder
private var inviterView: some View {
if let invitedText = attributedInviteText, let name = invite.roomDetails.inviter?.displayName {
HStack(alignment: .firstTextBaseline) {
HStack(alignment: .firstTextBaseline, spacing: 8) {
LoadableAvatarImage(url: invite.roomDetails.inviter?.avatarURL,
name: name,
contentID: name,
@ -85,7 +85,7 @@ struct InvitesScreenCell: View {
@ViewBuilder
private var textualContent: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 0) {
Text(title)
.font(.compound.bodyLGSemibold)
.foregroundColor(.compound.textPrimary)

View File

@ -24,7 +24,7 @@ struct OnboardingScreen: View {
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 0) {
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))
@ -36,6 +36,7 @@ struct OnboardingScreen: View {
.frame(width: geometry.size.width)
.padding(.bottom, UIConstants.actionButtonBottomPadding)
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16)
.padding(.top, 8)
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))
@ -49,7 +50,7 @@ struct OnboardingScreen: View {
}
var content: some View {
VStack {
VStack(spacing: 0) {
Spacer()
if verticalSizeClass == .regular {

View File

@ -24,7 +24,7 @@ struct RoomMembersListScreen: View {
var body: some View {
ScrollView {
LazyVStack(alignment: .leading) {
LazyVStack(alignment: .leading, spacing: 12) {
membersSection(data: context.viewState.visibleInvitedMembers, sectionTitle: L10n.screenRoomMemberListPendingHeaderTitle)
membersSection(data: context.viewState.visibleJoinedMembers, sectionTitle: L10n.screenRoomMemberListHeaderTitle(Int(context.viewState.joinedMembersCount)))
}
@ -54,7 +54,7 @@ struct RoomMembersListScreen: View {
Text(sectionTitle)
.foregroundColor(.compound.textSecondary)
.font(.compound.bodyLG)
.padding(.vertical, 12)
.padding(.top, 12)
}
}
.padding(.horizontal)

View File

@ -24,7 +24,7 @@ struct RoomMembersListScreenMemberCell: View {
Button {
context.send(viewAction: .selectMember(id: member.id))
} label: {
HStack {
HStack(spacing: 8) {
LoadableAvatarImage(url: member.avatarURL,
name: member.name ?? "",
contentID: member.id,
@ -36,26 +36,24 @@ struct RoomMembersListScreenMemberCell: View {
.font(.compound.bodyMDSemibold)
.foregroundColor(.compound.textPrimary)
.lineLimit(1)
Spacer()
}
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)
}
}
}
struct RoomMembersListMemberCell_Previews: PreviewProvider, TestablePreview {
static let members: [RoomMemberProxyMock] = [
.mockAlice,
.mockBob,
.mockCharlie
]
static let viewModel = RoomMembersListScreenViewModel(roomProxy: RoomProxyMock(with: .init(displayName: "Some room", members: members)),
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
static var previews: some View {
let members: [RoomMemberProxyMock] = [
.mockAlice,
.mockBob,
.mockCharlie
]
let viewModel = RoomMembersListScreenViewModel(roomProxy: RoomProxyMock(with: .init(displayName: "Some room", members: members)),
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
return VStack {
VStack(spacing: 12) {
ForEach(members, id: \.userID) { member in
RoomMembersListScreenMemberCell(member: .init(withProxy: member), context: viewModel.context)
}

View File

@ -72,7 +72,7 @@ struct LongPressWithFeedback_Previews: PreviewProvider, TestablePreview {
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
mockBubble("This is a message from somebody with a couple of lines of text.")
.longPressWithFeedback { isPresentingSheet = true }

View File

@ -33,7 +33,7 @@ private struct TimelineItemAccessibilityModifier: ViewModifier {
case let timelineItem as EventBasedTimelineItemProtocol:
content
.accessibilityRepresentation {
VStack {
VStack(spacing: 8) {
Text(timelineItem.sender.displayName ?? timelineItem.sender.id)
content
}

View File

@ -49,7 +49,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
}
VStack(alignment: alignment, spacing: 0) {
HStack {
HStack(spacing: 0) {
if timelineItem.isOutgoing {
Spacer()
}
@ -520,7 +520,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
}
static var replies: some View {
VStack {
VStack(spacing: 0) {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
timestamp: "10:42",
isOutgoing: true,

View File

@ -27,16 +27,12 @@ struct TimelineItemPlainStylerView<Content: View>: View {
@State private var showItemActionMenu = false
var body: some View {
VStack(alignment: .trailing) {
VStack(alignment: .trailing, spacing: 0) {
VStack(alignment: .leading, spacing: 4) {
header
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .firstTextBaseline) {
contentWithReply
Spacer()
}
contentWithReply
supplementaryViews
}
}
@ -47,7 +43,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
@ViewBuilder
var contentWithReply: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
if let messageTimelineItem = timelineItem as? EventBasedMessageTimelineItemProtocol {
if messageTimelineItem.isThreaded {
ThreadDecorator()
@ -98,8 +94,8 @@ struct TimelineItemPlainStylerView<Content: View>: View {
@ViewBuilder
private var header: some View {
if shouldShowSenderDetails {
HStack {
HStack {
HStack(spacing: 8) {
HStack(spacing: 8) {
TimelineSenderAvatarView(timelineItem: timelineItem)
Text(timelineItem.sender.displayName ?? timelineItem.sender.id)
.font(.subheadline)
@ -123,7 +119,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
@ViewBuilder
private var supplementaryViews: some View {
VStack {
VStack(spacing: 4) {
if timelineItem.properties.isEdited {
Text(L10n.commonEditedSuffix)
.font(.compound.bodySM)
@ -251,8 +247,9 @@ struct TimelineItemPlainStylerView_Previews: PreviewProvider, TestablePreview {
}
}
.environment(\.timelineStyle, .plain)
.previewLayout(.sizeThatFits)
.environmentObject(viewModel.context)
.previewLayout(.sizeThatFits)
threads
.padding()
.environment(\.timelineStyle, .plain)

View File

@ -145,7 +145,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
content: .init(body: "באמת‏! -- house!"))
static var testView: some View {
VStack {
VStack(spacing: 0) {
TextRoomTimelineView(timelineItem: base)
TextRoomTimelineView(timelineItem: sentNonLast)
TextRoomTimelineView(timelineItem: sentLast)
@ -156,7 +156,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
}
static var languagesTestView: some View {
VStack {
VStack(spacing: 0) {
TextRoomTimelineView(timelineItem: ltrString)
TextRoomTimelineView(timelineItem: rtlString)
TextRoomTimelineView(timelineItem: ltrStringThatContainsRtl)

View File

@ -67,7 +67,7 @@ struct ReactionsSummaryView: View {
TabView(selection: $selectedReactionKey) {
ForEach(reactions, id: \.self) { reaction in
ScrollView {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
ForEach(reaction.senders, id: \.self) { sender in
ReactionSummarySenderView(sender: sender, member: members[sender.senderID], imageProvider: imageProvider)
.padding(.horizontal, 16)
@ -122,15 +122,15 @@ private struct ReactionSummarySenderView: View {
}
var body: some View {
HStack {
HStack(spacing: 8) {
LoadableAvatarImage(url: member?.avatarURL,
name: displayName,
contentID: sender.senderID,
avatarSize: .user(on: .timeline),
imageProvider: imageProvider)
VStack(alignment: .leading) {
HStack {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 8) {
Text(displayName)
.font(.compound.bodyMDSemibold)
.frame(maxWidth: .infinity, alignment: .leading)

View File

@ -42,7 +42,7 @@ struct TimelineDeliveryStatusView: View {
struct TimelineDeliveryStatusView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack {
VStack(spacing: 8) {
TimelineDeliveryStatusView(deliveryStatus: .sending)
TimelineDeliveryStatusView(deliveryStatus: .sent)
}

View File

@ -205,7 +205,7 @@ struct TimelineReactionAddMoreButtonLabel: View {
struct TimelineReactionViewPreviewsContainer: View {
var body: some View {
VStack {
VStack(spacing: 8) {
TimelineReactionsView(context: RoomScreenViewModel.mock.context,
itemID: .init(timelineID: "1"),
reactions: [AggregatedReaction.mockReactionWithLongText,

View File

@ -92,7 +92,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
}
static var previews: some View {
VStack {
VStack(spacing: 8) {
TimelineReadReceiptsView(timelineItem: mockTimelineItem(with: singleReceipt))
.environmentObject(viewModel.context)
TimelineReadReceiptsView(timelineItem: mockTimelineItem(with: doubleReceipt))

View File

@ -119,7 +119,7 @@ struct SwipeRightAction_Previews: PreviewProvider, TestablePreview {
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 2) {
mockBubble("This is a message from somebody with a couple of lines of text.")
.swipeRightAction {
Image(systemName: "flame")

View File

@ -22,7 +22,7 @@ struct AudioRoomTimelineView: View {
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
HStack {
HStack(spacing: 8) {
Image(systemName: "waveform")
.foregroundColor(.compound.iconPrimary)
FormattedBodyText(text: timelineItem.content.body)

View File

@ -34,7 +34,7 @@ struct CollapsibleRoomTimelineView: View {
isExpanded.toggle()
}
} label: {
HStack(alignment: .center) {
HStack(alignment: .center, spacing: 8) {
Text(L10n.roomTimelineStateChanges(timelineItem.items.count))
Text(Image(systemName: "chevron.forward"))
.rotationEffect(.degrees(isExpanded ? 90 : 0))

View File

@ -60,7 +60,7 @@ private struct EncryptedHistoryLabelStyle: LabelStyle {
struct EncryptedHistoryRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack {
VStack(spacing: 8) {
EncryptedHistoryRoomTimelineView(timelineItem: .init(id: .random, isSessionVerified: true))
EncryptedHistoryRoomTimelineView(timelineItem: .init(id: .random, isSessionVerified: false))
}

View File

@ -22,7 +22,7 @@ struct FileRoomTimelineView: View {
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
HStack {
HStack(spacing: 8) {
Image(systemName: "doc.text.fill")
.foregroundColor(.compound.iconPrimary)
FormattedBodyText(text: timelineItem.content.body)

View File

@ -87,7 +87,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
ScrollView {
VStack {
VStack(spacing: 8) {
states
.padding(.horizontal)
}
@ -96,7 +96,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview {
.previewDisplayName("Bubbles")
ScrollView {
VStack {
VStack(spacing: 0) {
states
.padding(.horizontal)
}

View File

@ -26,7 +26,7 @@ struct PollOptionView: View {
FormRowAccessory(kind: .multipleSelection(isSelected: pollOption.isSelected))
VStack(spacing: 10) {
HStack(alignment: .lastTextBaseline) {
HStack(alignment: .lastTextBaseline, spacing: 8) {
Text(pollOption.text)
.font(isFinalWinningOption ? .compound.bodyLGSemibold : .compound.bodyLG)
.multilineTextAlignment(.leading)
@ -82,7 +82,7 @@ private struct PollProgressView: View {
struct PollOptionView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack {
VStack(spacing: 8) {
Group {
PollOptionView(pollOption: .init(id: "1",
text: "Italian 🇮🇹",

View File

@ -22,7 +22,7 @@ struct UnsupportedRoomTimelineView: View {
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
Label {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 0) {
Text("\(timelineItem.body): \(timelineItem.eventType)")
.fixedSize(horizontal: false, vertical: true)

View File

@ -24,7 +24,7 @@ struct TimelineItemDebugView: View {
var body: some View {
NavigationStack {
ScrollView {
VStack {
VStack(spacing: 8) {
TimelineItemInfoDisclosureGroup(title: "Model", text: info.model, isInitiallyExpanded: true)
if let originalJSONInfo = info.originalJSON {
@ -83,10 +83,9 @@ struct TimelineItemDebugView: View {
@ViewBuilder
var disclosureGroupContent: some View {
VStack(alignment: .leading) {
Spacer()
VStack(alignment: .leading, spacing: 0) {
Divider()
.padding(.vertical, 8)
Text(text)
.font(.compound.bodyXS.monospaced())

View File

@ -142,7 +142,7 @@ public struct TimelineItemMenu: View {
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy)
public var body: some View {
VStack {
VStack(spacing: 8) {
header
.frame(idealWidth: 300.0)
@ -187,7 +187,7 @@ public struct TimelineItemMenu: View {
Spacer(minLength: 8.0)
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 0) {
Text(item.sender.displayName ?? item.sender.id)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textPrimary)
@ -211,7 +211,7 @@ public struct TimelineItemMenu: View {
}
private var reactionsSection: some View {
HStack(alignment: .center) {
HStack(alignment: .center, spacing: 8) {
reactionButton(for: "👍️")
reactionButton(for: "👎️")
reactionButton(for: "🔥")
@ -286,12 +286,10 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
static let viewModel = RoomScreenViewModel.mock
static var previews: some View {
VStack {
if let item = RoomTimelineItemFixtures.singleMessageChunk.first as? EventBasedTimelineItemProtocol,
let actions = TimelineItemMenuActions(actions: [.copy, .edit, .reply(isThread: false), .redact], debugActions: [.viewSource]) {
TimelineItemMenu(item: item, actions: actions)
}
if let item = RoomTimelineItemFixtures.singleMessageChunk.first as? EventBasedTimelineItemProtocol,
let actions = TimelineItemMenuActions(actions: [.copy, .edit, .reply(isThread: false), .redact], debugActions: [.viewSource]) {
TimelineItemMenu(item: item, actions: actions)
.environmentObject(viewModel.context)
}
.environmentObject(viewModel.context)
}
}

View File

@ -52,7 +52,7 @@ struct SecureBackupKeyBackupScreen: View {
.font(.compound.bodyMD)
.multilineTextAlignment(.center)
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 10) {
Label {
Text(L10n.screenKeyBackupDisableDescriptionPoint1)
.foregroundColor(.compound.textSecondary)

View File

@ -121,19 +121,19 @@ struct SecureBackupRecoveryKeyScreen: View {
}
private var generateRecoveryKeySection: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
Text(L10n.commonRecoveryKey)
.foregroundColor(.compound.textPrimary)
.font(.compound.bodySM)
HStack {
Group {
if context.viewState.recoveryKey == nil {
Button(generateButtonTitle) {
context.send(viewAction: .generateKey)
}
.font(.compound.bodyLGSemibold)
} else {
HStack(alignment: .top) {
HStack(alignment: .top, spacing: 8) {
Text(context.viewState.recoveryKey ?? "")
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyLG)
@ -155,15 +155,16 @@ struct SecureBackupRecoveryKeyScreen: View {
.background(Color.compound.bgSubtleSecondaryLevel0)
.clipShape(RoundedRectangle(cornerRadius: 8))
HStack(alignment: .top) {
if context.viewState.recoveryKey == nil {
CompoundIcon(\.infoSolid, size: .small, relativeTo: .compound.bodySM)
}
Label {
Text(context.viewState.recoveryKeySubtitle)
.foregroundColor(.compound.textSecondary)
.font(.compound.bodySM)
} icon: {
if context.viewState.recoveryKey == nil {
CompoundIcon(\.infoSolid, size: .small, relativeTo: .compound.bodySM)
}
}
.labelStyle(.custom(spacing: 8, alignment: .top))
}
}
@ -173,7 +174,7 @@ struct SecureBackupRecoveryKeyScreen: View {
@ViewBuilder
private var confirmRecoveryKeySection: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
Text(L10n.commonRecoveryKey)
.foregroundColor(.compound.textPrimary)
.font(.compound.bodySM)

View File

@ -196,7 +196,7 @@ struct SettingsScreen: View {
})
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.logout)
} footer: {
VStack {
VStack(spacing: 0) {
versionText
.frame(maxWidth: .infinity)

View File

@ -41,18 +41,16 @@ struct VoiceMessageRoomPlaybackView: View {
}
var body: some View {
HStack {
HStack {
VoiceMessageButton(state: .init(playerState.playerButtonPlaybackState),
size: .medium,
action: onPlayPause)
Text(timeLabelContent)
.lineLimit(1)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textSecondary)
.monospacedDigit()
.fixedSize(horizontal: true, vertical: true)
}
HStack(spacing: 8) {
VoiceMessageButton(state: .init(playerState.playerButtonPlaybackState),
size: .medium,
action: onPlayPause)
Text(timeLabelContent)
.lineLimit(1)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textSecondary)
.monospacedDigit()
.fixedSize(horizontal: true, vertical: true)
waveformView
.waveformInteraction(isDragging: $isDragging,

View File

@ -58,7 +58,8 @@ class CreatePollScreenUITests: XCTestCase {
if app.keyboards.count > 0 {
app.typeText("\n")
}
app.swipeUp()
app.swipeUp() // Dismisses the keyboard.
app.swipeUp() // Ensures that the bottom is shown.
XCTAssertFalse(addOption.exists)
XCTAssertFalse(createButton.isEnabled)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More