From 43dafda3efd99852c428094342a261a795aad49c Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 24 Jul 2023 13:08:50 +0100 Subject: [PATCH] Tweaks for macOS (#1383) * Fix constant Create Room button animation on macOS. * Allow right-clicking on timeline items to work again on macOS. * Use inline media upload previews on macOS This comes with the caveat that the previews are no longer interactive. --- ElementX.xcodeproj/project.pbxproj | 6 +- .../en.lproj/Localizable.strings | 1 + ElementX/Sources/Generated/Strings.swift | 2 + .../SwiftUI/Animation/ShimmerModifier.swift | 8 +- .../Screens/HomeScreen/View/HomeScreen.swift | 2 - .../View/MediaUploadPreviewScreen.swift | 20 +++- .../Screens/RoomScreen/RoomScreenModels.swift | 8 +- .../RoomScreen/RoomScreenViewModel.swift | 14 ++- .../Style/TimelineItemBubbledStylerView.swift | 6 + .../Style/TimelineItemPlainStylerView.swift | 6 + .../View/TimelineItemMacContextMenu.swift | 46 ++++++++ .../RoomScreen/View/TimelineItemMenu.swift | 105 +++++++----------- .../RoomTimelineController.swift | 3 +- .../RoomTimelineItemFactory.swift | 2 +- changelog.d/pr-1383.change | 1 + 15 files changed, 144 insertions(+), 86 deletions(-) create mode 100644 ElementX/Sources/Screens/RoomScreen/View/TimelineItemMacContextMenu.swift create mode 100644 changelog.d/pr-1383.change diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index e6a139562..0017b8808 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C070FD43DC6BF4E50217965A /* LocalizationTests.swift */; }; + 020C530986D7B97631877FEF /* TimelineItemMacContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4AD793D50748F8997E5B15 /* TimelineItemMacContextMenu.swift */; }; 020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D7F513A36C9C1951DB44C /* AnalyticsSettingsScreen.swift */; }; 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; }; 02F4FAE40AF63A1941FD3BBA /* NotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B7F8EE25775DE2A305CBB5 /* NotificationCenterProtocol.swift */; }; @@ -1005,6 +1006,7 @@ 4959CECEC984B3995616F427 /* DataProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProtectionManager.swift; sourceTree = ""; }; 49D2C8E66E83EA578A7F318A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationRequest.swift; sourceTree = ""; }; + 4A4AD793D50748F8997E5B15 /* TimelineItemMacContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMacContextMenu.swift; sourceTree = ""; }; 4A542BC40D6EC2E66BC5659B /* TextBasedRoomTimelineViewMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineViewMock.swift; sourceTree = ""; }; 4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaLoader.swift; sourceTree = ""; }; 4AD6299F4516797E9BBE14C3 /* AnalyticsLocationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsLocationType.swift; sourceTree = ""; }; @@ -2585,6 +2587,7 @@ 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */, 4552D3466B1453F287223ADA /* SwipeRightAction.swift */, 7023EB4F3B7C7D1FBA68638B /* TimelineItemDebugView.swift */, + 4A4AD793D50748F8997E5B15 /* TimelineItemMacContextMenu.swift */, A1BF12A5E7C76777C4BF0F2B /* TimelineItemMenu.swift */, 0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */, F9212AE02CBDD692C56A879F /* TimelineTableViewController.swift */, @@ -3624,7 +3627,7 @@ path = Timeline; sourceTree = ""; }; - "TEMP_57725DF7-D55D-4DB9-8946-6ECB4F3AFE33" /* element-x-ios */ = { + "TEMP_62CE5EF1-8768-4933-9547-E5F60DAC11F4" /* element-x-ios */ = { isa = PBXGroup; children = ( 41553551C55AD59885840F0E /* secrets.xcconfig */, @@ -4613,6 +4616,7 @@ 84C0CF78BCE085C08CB94D86 /* TimelineEventProxy.swift in Sources */, 157E5FDDF419C0B2CA7E2C28 /* TimelineItemBubbledStylerView.swift in Sources */, FBCCF1EA25A071324FCD8544 /* TimelineItemDebugView.swift in Sources */, + 020C530986D7B97631877FEF /* TimelineItemMacContextMenu.swift in Sources */, 858C04B62166B5BAFCD20F2D /* TimelineItemMenu.swift in Sources */, F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */, 440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 1c71c610b..04ecb7a52 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -39,6 +39,7 @@ "action_open_with" = "Open with"; "action_quick_reply" = "Quick reply"; "action_quote" = "Quote"; +"action_react" = "React"; "action_remove" = "Remove"; "action_reply" = "Reply"; "action_report_bug" = "Report bug"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 1d43519a0..2e83f1b42 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -94,6 +94,8 @@ public enum L10n { public static var actionQuickReply: String { return L10n.tr("Localizable", "action_quick_reply") } /// Quote public static var actionQuote: String { return L10n.tr("Localizable", "action_quote") } + /// React + public static var actionReact: String { return L10n.tr("Localizable", "action_react") } /// Remove public static var actionRemove: String { return L10n.tr("Localizable", "action_remove") } /// Reply diff --git a/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift b/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift index 4e2c3076b..8926c4889 100644 --- a/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift +++ b/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift @@ -40,13 +40,15 @@ struct ShimmerModifier: ViewModifier { /// The colour that causes the view to remain unchanged. private let regularColor = Color.white + /// A slow linear animation which auto-repeats after a delay. + private let animation: Animation = Tests.isRunningUITests ? .noAnimation : .linear(duration: 1.75).delay(0.5).repeatForever(autoreverses: false) + func body(content: Content) -> some View { content .mask { gradient } + .animation(animation, value: animationTrigger) .task { - withElementAnimation(.linear(duration: 1.75).delay(0.5).repeatForever(autoreverses: false)) { - animationTrigger.toggle() - } + animationTrigger.toggle() } } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index d8bcbc73e..4ce7cd67a 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -126,8 +126,6 @@ struct HomeScreen: View { ToolbarItemGroup(placement: .bottomBar) { Spacer() newRoomButton - // Fix position animating on loop by getting caught up in the shimmer effect somehow. - .animation(.noAnimation, value: context.viewState.roomListMode) } } diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift index 08dab1b21..3261cd06a 100644 --- a/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift @@ -20,13 +20,19 @@ import SwiftUI struct MediaUploadPreviewScreen: View { @ObservedObject var context: MediaUploadPreviewScreenViewModel.Context + var title: String { + ProcessInfo.processInfo.isiOSAppOnMac ? context.viewState.title ?? "" : "" + } + var body: some View { PreviewView(context: context, fileURL: context.viewState.url, title: context.viewState.title) .id(UUID()) + .navigationTitle(title) + .navigationBarTitleDisplayMode(.inline) .disabled(context.viewState.shouldDisableInteraction) - .ignoresSafeArea(edges: .bottom) + .ignoresSafeArea(edges: [.horizontal, .bottom]) .toolbar { toolbar } .interactiveDismissDisabled() } @@ -52,15 +58,19 @@ private struct PreviewView: UIViewControllerRepresentable { let fileURL: URL let title: String? - func makeUIViewController(context: Context) -> UINavigationController { + func makeUIViewController(context: Context) -> UIViewController { let previewController = QLPreviewController() previewController.dataSource = context.coordinator previewController.delegate = context.coordinator - - return UINavigationController(rootViewController: previewController) + + if ProcessInfo.processInfo.isiOSAppOnMac { + return previewController + } else { + return UINavigationController(rootViewController: previewController) + } } - func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { } + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(view: self) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 9f49fa4e5..990e15494 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -95,12 +95,14 @@ struct RoomScreenViewState: BindableState { var readReceiptsEnabled: Bool var isEncryptedOneToOneRoom = false + var composerMode: RoomScreenComposerMode = .default + let scrollToBottomPublisher = PassthroughSubject() + var bindings: RoomScreenViewStateBindings + /// A closure providing the actions to show when long pressing on an item in the timeline. var timelineItemMenuActionProvider: (@MainActor (_ itemId: TimelineItemIdentifier) -> TimelineItemMenuActions?)? - var composerMode: RoomScreenComposerMode = .default - var sendButtonDisabled: Bool { bindings.composerText.count == 0 } @@ -112,8 +114,6 @@ struct RoomScreenViewState: BindableState { var itemViewModels: [RoomTimelineItemViewModel] { itemsDictionary.values.elements } - - let scrollToBottomPublisher = PassthroughSubject() } struct RoomScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 2a11cfeb0..b5a74d5c4 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -79,7 +79,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol var callback: ((RoomScreenViewModelAction) -> Void)? - // swiftlint:disable:next cyclomatic_complexity + // swiftlint:disable:next cyclomatic_complexity function_body_length override func process(viewAction: RoomScreenViewAction) { switch viewAction { case .displayRoomDetails: @@ -129,8 +129,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol case .tappedOnUser(userID: let userID): Task { await handleTappedUser(userID: userID) } case .displayEmojiPicker(let itemID): - guard let item = state.itemsDictionary[itemID.timelineID], item.isReactable else { return } - callback?(.displayEmojiPicker(itemID: itemID)) + showEmojiPicker(for: itemID) case .reactionSummary(let itemID, let key): showReactionSummary(for: itemID, selectedKey: key) case .retrySend(let itemID): @@ -501,6 +500,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } case .report: callback?(.displayReportContent(itemID: itemID, senderID: eventTimelineItem.sender.id)) + case .react: + showEmojiPicker(for: itemID) } if action.switchToDefaultComposer { @@ -662,7 +663,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } - // MARK: - Reaction summary + // MARK: - Reactions + + private func showEmojiPicker(for itemID: TimelineItemIdentifier) { + guard let item = state.itemsDictionary[itemID.timelineID], item.isReactable else { return } + callback?(.displayEmojiPicker(itemID: itemID)) + } private func showReactionSummary(for itemID: TimelineItemIdentifier, selectedKey: String) { guard let timelineItem = timelineController.timelineItems.first(where: { $0.id == itemID }), diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift index fb756d8cc..6fea8e8e6 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift @@ -139,6 +139,12 @@ struct TimelineItemBubbledStylerView: View { } action: { context.send(viewAction: .timelineItemMenuAction(itemID: timelineItem.id, action: .reply)) } + .contextMenu { + TimelineItemMacContextMenu(item: timelineItem, + actionProvider: context.viewState.timelineItemMenuActionProvider) { action in + context.send(viewAction: .timelineItemMenuAction(itemID: timelineItem.id, action: action)) + } + } .padding(.top, messageBubbleTopPadding) } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift index 2e2922273..9474b466c 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift @@ -81,6 +81,12 @@ struct TimelineItemPlainStylerView: View { } action: { context.send(viewAction: .timelineItemMenuAction(itemID: timelineItem.id, action: .reply)) } + .contextMenu { + TimelineItemMacContextMenu(item: timelineItem, + actionProvider: context.viewState.timelineItemMenuActionProvider) { action in + context.send(viewAction: .timelineItemMenuAction(itemID: timelineItem.id, action: action)) + } + } } @ViewBuilder diff --git a/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMacContextMenu.swift b/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMacContextMenu.swift new file mode 100644 index 000000000..66e93d3c2 --- /dev/null +++ b/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMacContextMenu.swift @@ -0,0 +1,46 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +/// The contents of the context menu shown when right clicking an item in the timeline on a Mac +struct TimelineItemMacContextMenu: View { + let item: RoomTimelineItemProtocol + let actionProvider: (@MainActor (_ itemId: TimelineItemIdentifier) -> TimelineItemMenuActions?)? + let send: (TimelineItemMenuAction) -> Void + + var body: some View { + if ProcessInfo.processInfo.isiOSAppOnMac { + if let menuActions = actionProvider?(item.id) { + Section { + if item.isReactable { + Button { send(.react) } label: { + TimelineItemMenuAction.react.label + } + } + ForEach(menuActions.actions) { action in + Button { send(action) } label: { action.label } + } + } + Section { + ForEach(menuActions.debugActions) { action in + Button { send(action) } label: { action.label } + } + } + } + } + } +} diff --git a/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMenu.swift b/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMenu.swift index ed5f06c87..03392f70b 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMenu.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/TimelineItemMenu.swift @@ -50,9 +50,11 @@ enum TimelineItemMenuAction: Identifiable, Hashable { case viewSource case retryDecryption(sessionID: String) case report + case react var id: Self { self } - + + /// Whether the item should cancel a reply/edit occurring in the composer. var switchToDefaultComposer: Bool { switch self { case .reply, .edit: @@ -61,7 +63,8 @@ enum TimelineItemMenuAction: Identifiable, Hashable { return true } } - + + /// Whether the action should be shown for an item that failed to send. var canAppearInFailedEcho: Bool { switch self { case .copy, .edit, .redact, .viewSource: @@ -70,7 +73,8 @@ enum TimelineItemMenuAction: Identifiable, Hashable { return false } } - + + /// Whether the action should be shown for a redacted item. var canAppearInRedacted: Bool { switch self { case .viewSource: @@ -79,20 +83,37 @@ enum TimelineItemMenuAction: Identifiable, Hashable { return false } } + + /// The item's label. + var label: some View { + switch self { + case .copy: return Label(L10n.actionCopy, systemImage: "doc.on.doc") + case .edit: return Label(L10n.actionEdit, systemImage: "pencil.line") + case .copyPermalink: return Label(L10n.actionCopyLinkToMessage, systemImage: "link") + case .reply: return Label(L10n.actionReply, systemImage: "arrowshape.turn.up.left") + case .forward: return Label(L10n.actionForward, systemImage: "arrowshape.turn.up.right") + case .redact: return Label(L10n.actionRemove, systemImage: "trash") + case .viewSource: return Label(L10n.actionViewSource, systemImage: "doc.text.below.ecg") + case .retryDecryption: return Label(L10n.actionRetryDecryption, systemImage: "arrow.down.message") + case .report: return Label(L10n.actionReportContent, systemImage: "exclamationmark.bubble") + case .react: return Label(L10n.actionReact, systemImage: "hand.thumbsup") + } + } +} + +extension RoomTimelineItemProtocol { + var isReactable: Bool { + guard let eventItem = self as? EventBasedTimelineItemProtocol else { return false } + return !eventItem.isRedacted && !eventItem.hasFailedToSend && !eventItem.hasFailedDecryption + } } public struct TimelineItemMenu: View { @EnvironmentObject private var context: RoomScreenViewModel.Context - @Environment(\.presentationMode) private var presentationMode + @Environment(\.dismiss) private var dismiss let item: EventBasedTimelineItemProtocol let actions: TimelineItemMenuActions - - private var canShowReactions: Bool { - !item.isRedacted && - !item.hasFailedToSend && - !item.hasFailedDecryption - } public var body: some View { VStack { @@ -104,7 +125,7 @@ public struct TimelineItemMenu: View { ScrollView { VStack(alignment: .leading, spacing: 0.0) { - if canShowReactions { + if item.isReactable { reactionsSection .padding(.top, 4.0) .padding(.bottom, 8.0) @@ -171,7 +192,7 @@ public struct TimelineItemMenu: View { reactionButton(for: "👏") Button { - presentationMode.wrappedValue.dismiss() + dismiss() // Otherwise we get errors that a sheet is already presented DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { context.send(viewAction: .displayEmojiPicker(itemID: item.id)) @@ -187,7 +208,7 @@ public struct TimelineItemMenu: View { private func reactionButton(for emoji: String) -> some View { Button { - presentationMode.wrappedValue.dismiss() + dismiss() context.send(viewAction: .toggleReaction(key: emoji, itemID: item.id)) } label: { Text(emoji) @@ -211,67 +232,23 @@ public struct TimelineItemMenu: View { private func viewsForActions(_ actions: [TimelineItemMenuAction]) -> some View { ForEach(actions, id: \.self) { action in - switch action { - case .copy: - Button { send(action) } label: { - MenuLabel(title: L10n.actionCopy, systemImageName: "doc.on.doc") - } - case .edit: - Button { send(action) } label: { - MenuLabel(title: L10n.actionEdit, systemImageName: "pencil.line") - } - case .copyPermalink: - Button { send(action) } label: { - MenuLabel(title: L10n.actionCopyLinkToMessage, systemImageName: "link") - } - case .reply: - Button { send(action) } label: { - MenuLabel(title: L10n.actionReply, systemImageName: "arrowshape.turn.up.left") - } - case .forward: - Button { send(action) } label: { - MenuLabel(title: L10n.actionForward, systemImageName: "arrowshape.turn.up.right") - } - case .redact: - Button(role: .destructive) { send(action) } label: { - MenuLabel(title: L10n.actionRemove, systemImageName: "trash") - } - case .viewSource: - Button { send(action) } label: { - MenuLabel(title: L10n.actionViewSource, systemImageName: "doc.text.below.ecg") - } - case .retryDecryption: - Button { send(action) } label: { - MenuLabel(title: L10n.actionRetryDecryption, systemImageName: "arrow.down.message") - } - case .report: - Button(role: .destructive) { send(action) } label: { - MenuLabel(title: L10n.actionReportContent, systemImageName: "exclamationmark.bubble") - } + Button { send(action) } label: { + action.label + .labelStyle(FixedIconSizeLabelStyle()) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() } } } private func send(_ action: TimelineItemMenuAction) { - presentationMode.wrappedValue.dismiss() + dismiss() // Otherwise we might get errors that a sheet is already presented DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { context.send(viewAction: .timelineItemMenuAction(itemID: item.id, action: action)) } } - - private struct MenuLabel: View { - let title: String - let systemImageName: String - - var body: some View { - Label(title, systemImage: systemImageName) - .labelStyle(FixedIconSizeLabelStyle()) - .multilineTextAlignment(.leading) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - } - } } struct TimelineItemMenu_Previews: PreviewProvider { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index a9f6f0c0c..d2b3c80d3 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -256,8 +256,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { return .none } } - - // swiftlint:disable:next cyclomatic_complexity function_body_length + private func updateTimelineItems() { var newTimelineItems = [RoomTimelineItemProtocol]() var canBackPaginate = true diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index c224a9864..27f9f65ab 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -36,7 +36,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { self.stateEventStringBuilder = stateEventStringBuilder } - // swiftlint:disable:next cyclomatic_complexity function_body_length + // swiftlint:disable:next cyclomatic_complexity func buildTimelineItem(for eventItemProxy: EventTimelineItemProxy) -> RoomTimelineItemProtocol? { let isOutgoing = eventItemProxy.isOwn diff --git a/changelog.d/pr-1383.change b/changelog.d/pr-1383.change new file mode 100644 index 000000000..e62879ece --- /dev/null +++ b/changelog.d/pr-1383.change @@ -0,0 +1 @@ +Tweaks for macOS only: Fix Create Room button animation bug / Restore the timeline context menu / Fix media upload preview obscuring send button. \ No newline at end of file