ios: scroll to reported item (#5915)

This commit is contained in:
spaced4ndy
2025-05-15 15:04:03 +00:00
committed by GitHub
parent 9225b896b0
commit 409d7e8532
14 changed files with 165 additions and 99 deletions

View File

@@ -294,16 +294,16 @@ struct CIFileView_Previews: PreviewProvider {
file: nil
)
Group {
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentFile, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: fileChatItemWtFile, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: fileChatItemWtFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 360))

View File

@@ -12,7 +12,7 @@ import SimpleXChat
struct CIImageView: View {
@EnvironmentObject var m: ChatModel
let chatItem: ChatItem
var scrollToItemId: ((ChatItem.ID) -> Void)? = nil
var scrollToItem: ((ChatItem.ID) -> Void)? = nil
var preview: UIImage?
let maxWidth: CGFloat
var imgWidth: CGFloat?
@@ -26,7 +26,7 @@ struct CIImageView: View {
if let uiImage = getLoadedImage(file) {
Group { if smallView { smallViewImageView(uiImage) } else { imageView(uiImage) } }
.fullScreenCover(isPresented: $showFullScreenImage) {
FullScreenMediaView(chatItem: chatItem, scrollToItemId: scrollToItemId, image: uiImage, showView: $showFullScreenImage)
FullScreenMediaView(chatItem: chatItem, scrollToItem: scrollToItem, image: uiImage, showView: $showFullScreenImage)
}
.if(!smallView) { view in
view.modifier(PrivacyBlur(blurred: $blurred))

View File

@@ -458,10 +458,10 @@ struct CIVoiceView_Previews: PreviewProvider {
duration: 30,
allowMenu: Binding.constant(true)
)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(), scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWtFile, scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWtFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
}
.previewLayout(.fixed(width: 360, height: 360))
}

View File

@@ -93,11 +93,11 @@ struct FramedCIVoiceView_Previews: PreviewProvider {
file: CIFile.getSample(fileStatus: .sndComplete)
)
Group {
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWithQuote, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWithQuote, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 360))

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ import AVKit
struct FullScreenMediaView: View {
@EnvironmentObject var m: ChatModel
@State var chatItem: ChatItem
var scrollToItemId: ((ChatItem.ID) -> Void)?
var scrollToItem: ((ChatItem.ID) -> Void)?
@State var image: UIImage?
@State var player: AVPlayer? = nil
@State var url: URL? = nil
@@ -71,7 +71,7 @@ struct FullScreenMediaView: View {
let w = abs(t.width)
if t.height > 60 && t.height > w * 2 {
showView = false
scrollToItemId?(chatItem.id)
scrollToItem?(chatItem.id)
} else if w > 60 && w > abs(t.height) * 2 && !scrolling {
let previous = t.width > 0
scrolling = true

View File

@@ -45,7 +45,8 @@ struct ChatItemView: View {
@Environment(\.showTimestamp) var showTimestamp: Bool
@Environment(\.revealed) var revealed: Bool
var chatItem: ChatItem
var scrollToItemId: (ChatItem.ID) -> Void
var scrollToItem: (ChatItem.ID) -> Void
@Binding var scrollToItemId: ChatItem.ID?
var maxWidth: CGFloat = .infinity
@Binding var allowMenu: Bool
@@ -53,7 +54,8 @@ struct ChatItemView: View {
chat: Chat,
im: ItemsModel,
chatItem: ChatItem,
scrollToItemId: @escaping (ChatItem.ID) -> Void,
scrollToItem: @escaping (ChatItem.ID) -> Void,
scrollToItemId: Binding<ChatItem.ID?> = .constant(nil),
showMember: Bool = false,
maxWidth: CGFloat = .infinity,
allowMenu: Binding<Bool> = .constant(false)
@@ -61,7 +63,8 @@ struct ChatItemView: View {
self.chat = chat
self.im = im
self.chatItem = chatItem
self.scrollToItemId = scrollToItemId
self.scrollToItem = scrollToItem
_scrollToItemId = scrollToItemId
self.maxWidth = maxWidth
_allowMenu = allowMenu
}
@@ -106,7 +109,8 @@ struct ChatItemView: View {
chat: chat,
im: im,
chatItem: chatItem,
scrollToItemId: scrollToItemId,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
preview: preview,
maxWidth: maxWidth,
imgWidth: adjustedMaxWidth,
@@ -272,15 +276,15 @@ struct ChatItemView_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
Group{
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getDeletedContentSample(), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getDeletedContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)).environment(\.revealed, true)
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 70))
@@ -303,7 +307,8 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItemId: { _ in }
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
)
ChatItemView(
chat: Chat.sampleData,
@@ -315,7 +320,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItemId: { _ in }
scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)
)
ChatItemView(
chat: Chat.sampleData,
@@ -327,7 +332,8 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItemId: { _ in }
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
)
ChatItemView(
chat: Chat.sampleData,
@@ -339,7 +345,8 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItemId: { _ in }
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
)
ChatItemView(
chat: Chat.sampleData,
@@ -351,7 +358,8 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItemId: { _ in }
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
)
}
.environment(\.revealed, true)

View File

@@ -25,6 +25,7 @@ struct ChatView: View {
@ObservedObject var im: ItemsModel
@State var mergedItems: BoxedValue<MergedItems>
@State var floatingButtonModel: FloatingButtonModel
@Binding var scrollToItemId: ChatItem.ID?
@State private var showChatInfoSheet: Bool = false
@State private var showAddMembersSheet: Bool = false
@State private var composeState = ComposeState()
@@ -198,6 +199,7 @@ struct ChatView: View {
groupInfo: groupInfo,
chat: chat,
groupMember: member,
scrollToItemId: $scrollToItemId,
navigation: true
)
}
@@ -233,7 +235,10 @@ struct ChatView: View {
}
) {
if let groupInfo = cInfo.groupInfo {
SecondaryChatView(chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: userSupportScopeInfo), chatItems: [], chatStats: ChatStats()))
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: userSupportScopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
}
}
.onAppear {
@@ -349,6 +354,16 @@ struct ChatView: View {
}
}
}
.if(im.secondaryIMFilter == nil) { v in
v.onChange(of: scrollToItemId) { itemId in
if let itemId = itemId {
dismissAllSheets(animated: false) {
scrollToItem(itemId)
scrollToItemId = nil
}
}
}
}
}
@inline(__always)
@@ -391,6 +406,7 @@ struct ChatView: View {
chat.created = Date.now
}
),
scrollToItemId: $scrollToItemId,
onSearch: { focusSearch() },
localAlias: groupInfo.localAlias
)
@@ -568,7 +584,7 @@ struct ChatView: View {
floatingButtonModel.updateOnListChange(scrollView.listState)
}
private func scrollToItemId(_ itemId: ChatItem.ID) {
private func scrollToItem(_ itemId: ChatItem.ID) {
Task {
do {
var index = mergedItems.boxedValue.indexInParentItems[itemId]
@@ -670,7 +686,8 @@ struct ChatView: View {
index: index,
isLastItem: index == mergedItems.boxedValue.items.count - 1,
chatItem: ci,
scrollToItemId: scrollToItemId,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
merged: mergedItem,
maxWidth: maxWidth,
composeState: $composeState,
@@ -1234,7 +1251,8 @@ struct ChatView: View {
let index: Int
let isLastItem: Bool
let chatItem: ChatItem
let scrollToItemId: (ChatItem.ID) -> Void
let scrollToItem: (ChatItem.ID) -> Void
@Binding var scrollToItemId: ChatItem.ID?
let merged: MergedItem
let maxWidth: CGFloat
@Binding var composeState: ComposeState
@@ -1594,7 +1612,8 @@ struct ChatView: View {
chat: chat,
im: im,
chatItem: ci,
scrollToItemId: scrollToItemId,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
maxWidth: maxWidth,
allowMenu: $allowMenu
)
@@ -2668,7 +2687,8 @@ struct ChatView_Previews: PreviewProvider {
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im)
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: Binding.constant(nil)
)
.environmentObject(chatModel)
}

View File

@@ -17,6 +17,7 @@ struct GroupChatInfoView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
@Binding var groupInfo: GroupInfo
@Binding var scrollToItemId: ChatItem.ID?
var onSearch: () -> Void
@State var localAlias: String
@FocusState private var aliasTextFieldFocused: Bool
@@ -96,11 +97,11 @@ struct GroupChatInfoView: View {
memberSupportButton()
}
if groupInfo.canModerate {
GroupReportsChatNavLink(chat: chat)
GroupReportsChatNavLink(chat: chat, scrollToItemId: $scrollToItemId)
}
if groupInfo.membership.memberActive
&& (groupInfo.membership.memberRole < .moderator || groupInfo.membership.supportChat != nil) {
UserSupportChatNavLink(chat: chat, groupInfo: groupInfo)
UserSupportChatNavLink(chat: chat, groupInfo: groupInfo, scrollToItemId: $scrollToItemId)
}
} header: {
Text("")
@@ -156,9 +157,16 @@ struct GroupChatInfoView: View {
let filteredMembers = s == ""
? members
: members.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) }
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: GMember(groupInfo.membership), user: true, alert: $alert)
MemberRowView(
chat: chat,
groupInfo: groupInfo,
groupMember: GMember(groupInfo.membership),
scrollToItemId: $scrollToItemId,
user: true,
alert: $alert
)
ForEach(filteredMembers) { member in
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: member, alert: $alert)
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: member, scrollToItemId: $scrollToItemId, alert: $alert)
}
}
@@ -365,6 +373,7 @@ struct GroupChatInfoView: View {
var chat: Chat
var groupInfo: GroupInfo
@ObservedObject var groupMember: GMember
@Binding var scrollToItemId: ChatItem.ID?
@EnvironmentObject var theme: AppTheme
var user: Bool = false
@Binding var alert: GroupChatInfoViewAlert?
@@ -427,7 +436,7 @@ struct GroupChatInfoView: View {
}
private func memberInfoView() -> some View {
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember)
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember, scrollToItemId: $scrollToItemId)
.navigationBarHidden(false)
}
@@ -540,12 +549,16 @@ struct GroupChatInfoView: View {
@EnvironmentObject var theme: AppTheme
var groupInfo: GroupInfo
@EnvironmentObject var chatModel: ChatModel
@Binding var scrollToItemId: ChatItem.ID?
@State private var navLinkActive = false
var body: some View {
let scopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: nil)
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()))
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
HStack {
Label("Chat with admins", systemImage: chat.supportUnreadCount > 0 ? "flag.fill" : "flag")
@@ -565,7 +578,7 @@ struct GroupChatInfoView: View {
private func memberSupportButton() -> some View {
NavigationLink {
MemberSupportView(groupInfo: groupInfo)
MemberSupportView(groupInfo: groupInfo, scrollToItemId: $scrollToItemId)
.navigationBarTitle("Chats with members")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
@@ -588,10 +601,11 @@ struct GroupChatInfoView: View {
@EnvironmentObject var theme: AppTheme
@State private var navLinkActive = false
@ObservedObject var chat: Chat
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(chat: chat)
SecondaryChatView(chat: chat, scrollToItemId: $scrollToItemId)
} label: {
HStack {
Label {
@@ -895,6 +909,7 @@ struct GroupChatInfoView_Previews: PreviewProvider {
GroupChatInfoView(
chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []),
groupInfo: Binding.constant(GroupInfo.sampleData),
scrollToItemId: Binding.constant(nil),
onSearch: {},
localAlias: ""
)

View File

@@ -16,6 +16,7 @@ struct GroupMemberInfoView: View {
@State var groupInfo: GroupInfo
@ObservedObject var chat: Chat
@ObservedObject var groupMember: GMember
@Binding var scrollToItemId: ChatItem.ID?
var navigation: Bool = false
@State private var connectionStats: ConnectionStats? = nil
@State private var connectionCode: String? = nil
@@ -105,7 +106,7 @@ struct GroupMemberInfoView: View {
Section {
if groupInfo.membership.memberRole >= .moderator
&& (member.memberRole < .moderator || member.supportChat != nil) {
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember)
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember, scrollToItemId: $scrollToItemId)
}
if let code = connectionCode { verifyCodeButton(code) }
if let connStats = connectionStats,
@@ -482,12 +483,16 @@ struct GroupMemberInfoView: View {
@EnvironmentObject var theme: AppTheme
var groupInfo: GroupInfo
var member: GMember
@Binding var scrollToItemId: ChatItem.ID?
@State private var navLinkActive = false
var body: some View {
let scopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: member.wrapped)
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()))
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
Label("Chat with member", systemImage: "flag")
}
@@ -846,7 +851,8 @@ struct GroupMemberInfoView_Previews: PreviewProvider {
GroupMemberInfoView(
groupInfo: GroupInfo.sampleData,
chat: Chat.sampleData,
groupMember: GMember.sampleData
groupMember: GMember.sampleData,
scrollToItemId: Binding.constant(nil)
)
}
}

View File

@@ -15,6 +15,7 @@ struct MemberSupportView: View {
@State private var searchText: String = ""
@FocusState private var searchFocussed
var groupInfo: GroupInfo
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
viewBody()
@@ -53,7 +54,8 @@ struct MemberSupportView: View {
ForEach(filteredMembersWithChats) { memberWithChat in
MemberSupportChatNavLink(
groupInfo: groupInfo,
memberWithChat: memberWithChat
memberWithChat: memberWithChat,
scrollToItemId: $scrollToItemId
)
}
}
@@ -66,6 +68,7 @@ struct MemberSupportView: View {
@State private var memberSupportChatNavLinkActive = false
var groupInfo: GroupInfo
var memberWithChat: GMember
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
ZStack {
@@ -79,7 +82,10 @@ struct MemberSupportView: View {
}
NavigationLink(isActive: $memberSupportChatNavLinkActive) {
SecondaryChatView(chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()))
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
EmptyView()
}
@@ -253,6 +259,7 @@ func showRemoveMemberAlert(_ groupInfo: GroupInfo, _ member: GroupMember, dismis
#Preview {
MemberSupportView(
groupInfo: GroupInfo.sampleData
groupInfo: GroupInfo.sampleData,
scrollToItemId: Binding.constant(nil)
)
}

View File

@@ -12,6 +12,7 @@ import SimpleXChat
struct SecondaryChatView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var chat: Chat
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
if let im = chatModel.secondaryIM {
@@ -19,7 +20,8 @@ struct SecondaryChatView: View {
chat: chat,
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im)
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: $scrollToItemId
)
.onDisappear {
chatModel.secondaryIM = nil
@@ -34,6 +36,7 @@ struct SecondaryChatView: View {
chatInfo: .group(groupInfo: GroupInfo.sampleData, groupChatScope: .memberSupport(groupMember_: GroupMember.sampleData)),
chatItems: [],
chatStats: ChatStats()
)
),
scrollToItemId: Binding.constant(nil)
)
}

View File

@@ -148,6 +148,7 @@ struct ChatListView: View {
@State private var userPickerShown: Bool = false
@State private var sheet: SomeSheet<AnyView>? = nil
@StateObject private var chatTagsModel = ChatTagsModel.shared
@State private var scrollToItemId: ChatItem.ID? = nil
// iOS 15 is required it to show/hide toolbar while chat is hidden/visible
@State private var viewOnScreen = true
@@ -451,7 +452,8 @@ struct ChatListView: View {
chat: chat,
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im)
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: $scrollToItemId
)
}
}

View File

@@ -367,13 +367,13 @@ struct ChatThemePreview: View {
let alice = ChatItem.getSample(1, CIDirection.directRcv, Date.now, NSLocalizedString("Good afternoon!", comment: "message preview"))
let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir))
HStack {
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: alice, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: alice, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
.modifier(ChatItemClipped(alice, tailVisible: true))
Spacer()
}
HStack {
Spacer()
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: bob, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: bob, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
.modifier(ChatItemClipped(bob, tailVisible: true))
.frame(alignment: .trailing)
}