mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Extract the timeline scroll to bottom button into its own view.
This commit is contained in:
committed by
Stefan Ceriu
parent
952e4f86c2
commit
e86fdaacaf
@@ -1286,6 +1286,7 @@
|
|||||||
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
|
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
|
||||||
FCF95603F1D056B1B106A415 /* AdvancedSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E2B20431F890ED64255CA1 /* AdvancedSettingsScreenViewModelProtocol.swift */; };
|
FCF95603F1D056B1B106A415 /* AdvancedSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E2B20431F890ED64255CA1 /* AdvancedSettingsScreenViewModelProtocol.swift */; };
|
||||||
FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; };
|
FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; };
|
||||||
|
FD439E183A48BE871AEEFAEA /* TimelineScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10765FBC83B34A3BC4ADB23 /* TimelineScrollToBottomButton.swift */; };
|
||||||
FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
|
FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
|
||||||
FD573B5D665824EB79EABF06 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327E3B3C58BEB0E65F4CF98 /* Observable.swift */; };
|
FD573B5D665824EB79EABF06 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327E3B3C58BEB0E65F4CF98 /* Observable.swift */; };
|
||||||
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
|
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
|
||||||
@@ -2480,6 +2481,7 @@
|
|||||||
E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFilterModels.swift; sourceTree = "<group>"; };
|
E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFilterModels.swift; sourceTree = "<group>"; };
|
||||||
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
|
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
|
||||||
E0FF9CB3EFA753277291F609 /* EncryptionResetScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetScreenCoordinator.swift; sourceTree = "<group>"; };
|
E0FF9CB3EFA753277291F609 /* EncryptionResetScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
E10765FBC83B34A3BC4ADB23 /* TimelineScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineScrollToBottomButton.swift; sourceTree = "<group>"; };
|
||||||
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileListRow.swift; sourceTree = "<group>"; };
|
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileListRow.swift; sourceTree = "<group>"; };
|
||||||
E157152B11E347F735C3FD6E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
E157152B11E347F735C3FD6E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
|
E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -3354,6 +3356,7 @@
|
|||||||
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
||||||
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */,
|
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */,
|
||||||
A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */,
|
A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */,
|
||||||
|
E10765FBC83B34A3BC4ADB23 /* TimelineScrollToBottomButton.swift */,
|
||||||
16D353E10A64172D863769BF /* TombstonedAvatarImage.swift */,
|
16D353E10A64172D863769BF /* TombstonedAvatarImage.swift */,
|
||||||
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */,
|
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */,
|
||||||
AD529C89924EE32CE307F36F /* VisualListItem.swift */,
|
AD529C89924EE32CE307F36F /* VisualListItem.swift */,
|
||||||
@@ -7786,6 +7789,7 @@
|
|||||||
8446C2A7ECEFDA79F622725F /* TimelineReactionsView.swift in Sources */,
|
8446C2A7ECEFDA79F622725F /* TimelineReactionsView.swift in Sources */,
|
||||||
4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */,
|
4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */,
|
||||||
6EB46C92ECFEAE71959D91D2 /* TimelineReplyView.swift in Sources */,
|
6EB46C92ECFEAE71959D91D2 /* TimelineReplyView.swift in Sources */,
|
||||||
|
FD439E183A48BE871AEEFAEA /* TimelineScrollToBottomButton.swift in Sources */,
|
||||||
9FBE1FB20171012260A32492 /* TimelineSenderAvatarView.swift in Sources */,
|
9FBE1FB20171012260A32492 /* TimelineSenderAvatarView.swift in Sources */,
|
||||||
C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */,
|
C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */,
|
||||||
785613C0C092B532198EB3BB /* TimelineStartRoomTimelineView.swift in Sources */,
|
785613C0C092B532198EB3BB /* TimelineStartRoomTimelineView.swift in Sources */,
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright 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 SwiftUI
|
||||||
|
|
||||||
|
struct TimelineScrollToBottomButton: View {
|
||||||
|
let isVisible: Bool
|
||||||
|
let callback: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button { callback() } label: {
|
||||||
|
Image(systemName: "chevron.down")
|
||||||
|
.font(.compound.bodyLG)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.compound.iconSecondary)
|
||||||
|
.padding(13)
|
||||||
|
.offset(y: 1)
|
||||||
|
.background {
|
||||||
|
Circle()
|
||||||
|
.fill(Color.compound.iconOnSolidPrimary)
|
||||||
|
// Intentionally using system primary colour to get white/black.
|
||||||
|
.shadow(color: .primary.opacity(0.33), radius: 2.0)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.opacity(isVisible ? 0.0 : 1.0)
|
||||||
|
.accessibilityHidden(isVisible)
|
||||||
|
.animation(.elementDefault, value: isVisible)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,10 @@ struct RoomScreen: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineView(timelineContext: timelineContext)
|
TimelineView(timelineContext: timelineContext)
|
||||||
.overlay(alignment: .bottomTrailing) {
|
.overlay(alignment: .bottomTrailing) {
|
||||||
scrollToBottomButton
|
TimelineScrollToBottomButton(isVisible: isAtBottomAndLive) {
|
||||||
|
timelineContext.send(viewAction: .scrollToBottom)
|
||||||
|
}
|
||||||
|
.accessibilityIdentifier(A11yIdentifiers.roomScreen.scrollToBottom)
|
||||||
}
|
}
|
||||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||||
.overlay(alignment: .top) {
|
.overlay(alignment: .top) {
|
||||||
@@ -123,28 +126,6 @@ struct RoomScreen: View {
|
|||||||
context.send(viewAction: .viewKnockRequests)
|
context.send(viewAction: .viewKnockRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scrollToBottomButton: some View {
|
|
||||||
Button { timelineContext.send(viewAction: .scrollToBottom) } label: {
|
|
||||||
Image(systemName: "chevron.down")
|
|
||||||
.font(.compound.bodyLG)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.compound.iconSecondary)
|
|
||||||
.padding(13)
|
|
||||||
.offset(y: 1)
|
|
||||||
.background {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.compound.iconOnSolidPrimary)
|
|
||||||
// Intentionally using system primary colour to get white/black.
|
|
||||||
.shadow(color: .primary.opacity(0.33), radius: 2.0)
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.opacity(isAtBottomAndLive ? 0.0 : 1.0)
|
|
||||||
.accessibilityHidden(isAtBottomAndLive)
|
|
||||||
.animation(.elementDefault, value: isAtBottomAndLive)
|
|
||||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.scrollToBottom)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isAtBottomAndLive: Bool {
|
private var isAtBottomAndLive: Bool {
|
||||||
timelineContext.isScrolledToBottom && timelineContext.viewState.timelineState.isLive
|
timelineContext.isScrolledToBottom && timelineContext.viewState.timelineState.isLive
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ struct ThreadTimelineScreen: View {
|
|||||||
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
||||||
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
||||||
.overlay(alignment: .bottomTrailing) {
|
.overlay(alignment: .bottomTrailing) {
|
||||||
scrollToBottomButton
|
TimelineScrollToBottomButton(isVisible: isAtBottomAndLive) {
|
||||||
|
timelineContext.send(viewAction: .scrollToBottom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||||
composer
|
composer
|
||||||
@@ -75,28 +77,6 @@ struct ThreadTimelineScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scrollToBottomButton: some View {
|
|
||||||
Button { timelineContext.send(viewAction: .scrollToBottom) } label: {
|
|
||||||
Image(systemName: "chevron.down")
|
|
||||||
.font(.compound.bodyLG)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.compound.iconSecondary)
|
|
||||||
.padding(13)
|
|
||||||
.offset(y: 1)
|
|
||||||
.background {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.compound.iconOnSolidPrimary)
|
|
||||||
// Intentionally using system primary colour to get white/black.
|
|
||||||
.shadow(color: .primary.opacity(0.33), radius: 2.0)
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.opacity(isAtBottomAndLive ? 0.0 : 1.0)
|
|
||||||
.accessibilityHidden(isAtBottomAndLive)
|
|
||||||
.animation(.elementDefault, value: isAtBottomAndLive)
|
|
||||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.scrollToBottom)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isAtBottomAndLive: Bool {
|
private var isAtBottomAndLive: Bool {
|
||||||
timelineContext.isScrolledToBottom && timelineContext.viewState.timelineState.isLive
|
timelineContext.isScrolledToBottom && timelineContext.viewState.timelineState.isLive
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user