mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
eae464ecd7
* Timeline: add a Jump to Unread button and a new-message badge on the scroll-to-bottom button. * Rename scrollToFirstUnread to scrollToReadMarker for consistency with isReadMarkerVisible. * Cache readMarkerUniqueID and unreadMessageCount as stored properties on TimelineState. * TimelineViewModel: rename newTimelineItems and drop redundant ordering comment. * Use Compound chevron icons in TimelineScrollButton. * Fix TimelineScrollButton layout at large dynamic type sizes. * Add a trailingAccessory slot to topBanners and use it to dodge banners with the jump-to-read-marker button. * Put jump-to-read-marker behind a developer-options feature flag. * Fine-tune TimelineScrollButton sizing and badge colour. * Remove TimelineScrollButton chevron offset that was compensating for SF Symbol centering. * Drop the new-messages count, replace with a presence dot. * Move both timeline scroll buttons to bottom-right and revert topBanners trailingAccessory slot. * Position TimelineScrollButton badge above for up direction and below for down direction. * Add long-press mark-as-read pill to the timeline scroll buttons * Read isSwitchingTimelines from state in updateHasNewMessagesAtBottom. * Use assertionFailure when scrollToReadMarker is called with no read marker. * Add TimelineScrollButton preview snapshots. * (fix) design and various self-review issues * (fix) suppress scroll button tap after long-press * (feat) fall back to RoomInfo.fullyReadEventID for jump-to-unread * (fix) hide jump-to-unread immediately after mark-as-read * (fix) align with newer matrix-rust-sdk surface * Bump the RustSDK to v26.05.20 --------- Signed-off-by: Daniel Anderson <daniel.anderson@toptal.com> Co-authored-by: Stefan Ceriu <stefan.ceriu@gmail.com>
81 lines
3.4 KiB
Swift
81 lines
3.4 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import Compound
|
|
import SwiftUI
|
|
|
|
struct ThreadTimelineScreen: View {
|
|
@ObservedObject private var context: ThreadTimelineScreenViewModelType.Context
|
|
@ObservedObject private var timelineContext: TimelineViewModelType.Context
|
|
private let composerToolbar: ComposerToolbar
|
|
|
|
init(context: ThreadTimelineScreenViewModelType.Context,
|
|
timelineContext: TimelineViewModelType.Context,
|
|
composerToolbar: ComposerToolbar) {
|
|
self.context = context
|
|
self.timelineContext = timelineContext
|
|
self.composerToolbar = composerToolbar
|
|
}
|
|
|
|
var body: some View {
|
|
TimelineView(timelineContext: timelineContext)
|
|
.background(.compound.bgCanvasDefault)
|
|
.toolbarRole(RoomHeaderView.toolbarRole)
|
|
.navigationTitle(L10n.commonThread)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar { toolbar }
|
|
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
|
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
|
.overlay(alignment: .top) {
|
|
FloatingDateBadge(dateText: timelineContext.floatingDate?.formattedDateSeparator()) {
|
|
timelineContext.send(viewAction: .scrollToFirstItemForCurrentDate)
|
|
}
|
|
.padding(.top, 13)
|
|
}
|
|
.overlay(alignment: .bottomTrailing) {
|
|
TimelineScrollButton(isHidden: !timelineContext.viewState.shouldShowScrollToBottomButton) {
|
|
timelineContext.send(viewAction: .scrollToBottom)
|
|
}
|
|
}
|
|
.safeAreaInset(edge: .bottom, spacing: 0) {
|
|
composer
|
|
.padding(.top, 8)
|
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
|
.environmentObject(timelineContext)
|
|
.environment(\.timelineContext, timelineContext)
|
|
// Make sure the reply header honours the hideTimelineMedia setting too.
|
|
.environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var composer: some View {
|
|
if context.viewState.canSendMessage {
|
|
composerToolbar
|
|
} else {
|
|
ComposerDisabledView()
|
|
}
|
|
}
|
|
|
|
@ToolbarContentBuilder
|
|
private var toolbar: some ToolbarContent {
|
|
// .principal + .primaryAction works better than .navigation leading + trailing
|
|
// as the latter disables interaction in the action button for rooms with long names
|
|
ToolbarItem(placement: .principal) {
|
|
RoomHeaderView(roomName: L10n.commonThread,
|
|
roomSubtitle: context.viewState.roomTitle,
|
|
roomAvatar: context.viewState.roomAvatar,
|
|
dmRecipientVerificationState: context.viewState.dmRecipientVerificationState,
|
|
roomHistorySharingState: context.viewState.roomHistorySharingState,
|
|
mediaProvider: context.mediaProvider) {
|
|
// There is no action but the iOS 26 designs have it looking like a button.
|
|
}
|
|
}
|
|
}
|
|
}
|