SimpleX-Chat/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift

156 lines
5.5 KiB
Swift
Raw Normal View History

//
// 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
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 553be124d5233c6afad18ef37f81fce659958101. * 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 895218b97802669c10d22028d22b9da8a8c1f01a. * progress indicator * optimization * disable scroll helper * experiment * Revert "experiment" This reverts commit c952c9e6230a05b639ab812431781f1353c94c5d. * 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-18 01:21:40 +07:00
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
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 553be124d5233c6afad18ef37f81fce659958101. * 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 895218b97802669c10d22028d22b9da8a8c1f01a. * progress indicator * optimization * disable scroll helper * experiment * Revert "experiment" This reverts commit c952c9e6230a05b639ab812431781f1353c94c5d. * 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-18 01:21:40 +07:00
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 } // this is used in full screen view, onTapGesture works
}
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()
}
}
}