SimpleX-Chat/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift
Stanislav Dmitrenko 9d1329498b
ios: open chat on first unread, "scroll" to quoted items that were not loaded (#5392)
* ios: open chat on first unread, "scroll" to quoted items that were not loaded

* more changes

* changes

* unused

* fix reveal logic

* debug

* changes

* test

* Revert "test"

This reverts commit 553be124d5.

* change

* change

* changes

* changes

* changes

* commented deceleration logic

* changes

* fixes

* optimized item identifiers to use merged item directly

* fixed counters

* encreased initial and preload counters

* fix initial loading and trimming items

* optimize

* allow marking read

* 10 instead of 5

* performance

* one more parameter in hash

* disable trimming

* performance

* performance - in background

* optimization

* next/prev

* changes

* markread

* finally

* less logs

* read

* change after merge

* trimming, edge cases

* wait until items loaded

* Revert "wait until items loaded"

This reverts commit 895218b978.

* progress indicator

* optimization

* disable scroll helper

* experiment

* Revert "experiment"

This reverts commit c952c9e623.

* jump

* no read

* layoutIfNeeded

* changes

* EndlessScrollView

* read

* changes

* changes

* changes

* reduce time to open a chat (by ~300ms)

* open from the first unread when clicking member chat

* refactored and removed unused code

* handling search emptiness to scroll to correct position

* changes

* read state maintain

* remove protocol

* avoid parsing chatId

* pass chat

* changes

* remove reveal

* refactor spaghetti

* remove ItemsScrollModel

* rename

* remove setUpdateListener

* unused

* optimization

* scrollToTop

* fix

* scrollbar working again

* scrollToBottom

* fix

* scrollBar hiding when not many items on screen

* small safer change

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-02-17 18:21:40 +00:00

155 lines
5.4 KiB
Swift

//
// FullScreenImageView.swift
// SimpleX (iOS)
//
// Created by Evgeny on 08/10/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
import SwiftyGif
import AVKit
struct FullScreenMediaView: View {
@EnvironmentObject var m: ChatModel
@State var chatItem: ChatItem
var scrollToItemId: ((ChatItem.ID) -> Void)?
@State var image: UIImage?
@State var player: AVPlayer? = nil
@State var url: URL? = nil
@Binding var showView: Bool
@State private var showNext = false
@State private var nextImage: UIImage?
@State private var nextPlayer: AVPlayer?
@State private var nextURL: URL?
@State private var scrolling = false
@State private var offset: CGFloat = 0
@State private var nextOffset: CGFloat = 0
var body: some View {
GeometryReader(content: mediaScrollView)
}
func mediaScrollView(_ g: GeometryProxy) -> some View {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
if showNext, let nextImage = nextImage {
if let image = image {
imageView(image).offset(x: offset)
} else if let player = player, let url = url {
videoView(player, url).offset(x: offset)
}
imageView(nextImage).offset(x: offset + nextOffset)
} else if showNext, let nextPlayer = nextPlayer, let nextURL = nextURL {
if let image = image {
imageView(image).offset(x: offset)
} else if let player = player, let url = url {
videoView(player, url).offset(x: offset)
}
videoView(nextPlayer, nextURL).offset(x: offset + nextOffset)
} else {
ZoomableScrollView {
if let image = image {
imageView(image)
} else if let player = player, let url = url {
videoView(player, url)
}
}
}
}
.onAppear {
startPlayerAndNotify()
}
.onDisappear {
player?.pause()
}
.gesture(
DragGesture(minimumDistance: 80)
.onChanged { gesture in
let t = gesture.translation
let w = abs(t.width)
if t.height > 60 && t.height > w * 2 {
showView = false
scrollToItemId?(chatItem.id)
} else if w > 60 && w > abs(t.height) * 2 && !scrolling {
let previous = t.width > 0
scrolling = true
if let item = m.nextChatItemData(chatItem.id, previous: previous, map: chatItemImage) {
var img: UIImage?
var url: URL?
(chatItem, img, url) = item
nextImage = img
nextPlayer?.pause()
if let url = url {
nextPlayer = VideoPlayerView.getOrCreatePlayer(url, true)
} else {
nextPlayer = nil
}
nextURL = url
let s = g.size.width
var toOffset: CGFloat
(toOffset, nextOffset) = previous ? (s, -s) : (-s, s)
showNext = true
withAnimation(.easeIn(duration: 0.2)) {
offset = toOffset
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
image = img
player?.pause()
self.url = url
if let url = url {
player = VideoPlayerView.getOrCreatePlayer(url, true)
startPlayerAndNotify()
} else {
player = nil
}
showNext = false
offset = 0
}
}
}
}
.onEnded { _ in scrolling = false }
)
}
private func imageView(_ img: UIImage) -> some View {
ZStack {
Color.black
if img.imageData == nil {
Image(uiImage: img)
.resizable()
.scaledToFit()
} else {
SwiftyGif(image: img)
.scaledToFit()
}
}
.onTapGesture { showView = false }
}
private func videoView( _ player: AVPlayer, _ url: URL) -> some View {
VideoPlayerView(player: player, url: url, showControls: true)
}
private func chatItemImage(_ ci: ChatItem) -> (ChatItem, UIImage?, URL?)? {
if case .image = ci.content.msgContent,
let img = getLoadedImage(ci.file) {
return (ci, img, nil)
}
// Currently, video support in gallery is not enabled
/*else if case .video = ci.content.msgContent,
let url = getLoadedVideo(ci.file) {
return (ci, nil, url)
}*/
return nil
}
private func startPlayerAndNotify() {
if let player = player {
m.stopPreviousRecPlay = url
player.play()
}
}
}