mv-experiment #1
|
@ -1,13 +1,17 @@
|
||||||
|
import AVFoundation
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import GRDB
|
import GRDB
|
||||||
|
import Photos
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class ConversationStore: ObservableObject {
|
final class ConversationStore: ObservableObject {
|
||||||
@Published private(set) var messages: [Message] = []
|
@Published private(set) var messages: [Message] = []
|
||||||
@Published var replyText = ""
|
@Published var replyText = ""
|
||||||
private(set) var roster: Roster
|
@Published var cameraAccessGranted = false
|
||||||
|
@Published var galleryAccessGranted = false
|
||||||
|
|
||||||
|
private(set) var roster: Roster
|
||||||
private let client: Client
|
private let client: Client
|
||||||
private let blockSize = Const.messagesPageSize
|
private let blockSize = Const.messagesPageSize
|
||||||
private let messagesMax = Const.messagesMaxSize
|
private let messagesMax = Const.messagesMaxSize
|
||||||
|
@ -49,6 +53,27 @@ extension ConversationStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ConversationStore {
|
||||||
|
func checkCameraAuthorization() async {
|
||||||
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
||||||
|
var isAuthorized = status == .authorized
|
||||||
|
if status == .notDetermined {
|
||||||
|
isAuthorized = await AVCaptureDevice.requestAccess(for: .video)
|
||||||
|
}
|
||||||
|
cameraAccessGranted = isAuthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkGalleryAuthorization() async {
|
||||||
|
let status = PHPhotoLibrary.authorizationStatus()
|
||||||
|
var isAuthorized = status == .authorized
|
||||||
|
if status == .notDetermined {
|
||||||
|
let req = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
|
||||||
|
isAuthorized = (req == .authorized) || (req == .limited)
|
||||||
|
}
|
||||||
|
galleryAccessGranted = isAuthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension ConversationStore {
|
private extension ConversationStore {
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
messagesCancellable = ValueObservation.tracking(Message
|
messagesCancellable = ValueObservation.tracking(Message
|
||||||
|
@ -67,26 +92,3 @@ private extension ConversationStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// var isAuthorized: Bool {
|
|
||||||
// get async {
|
|
||||||
// let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
||||||
//
|
|
||||||
// // Determine if the user previously authorized camera access.
|
|
||||||
// var isAuthorized = status == .authorized
|
|
||||||
//
|
|
||||||
// // If the system hasn't determined the user's authorization status,
|
|
||||||
// // explicitly prompt them for approval.
|
|
||||||
// if status == .notDetermined {
|
|
||||||
// isAuthorized = await AVCaptureDevice.requestAccess(for: .video)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return isAuthorized
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// func setUpCaptureSession() async {
|
|
||||||
// guard await isAuthorized else { return }
|
|
||||||
// // Set up the capture session.
|
|
||||||
// }
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ import SwiftUI
|
||||||
|
|
||||||
struct CameraCellPreview: View {
|
struct CameraCellPreview: View {
|
||||||
@Environment(\.router) var router
|
@Environment(\.router) var router
|
||||||
@State private var cameraAuthorized = false
|
@EnvironmentObject var store: ConversationStore
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if cameraAuthorized {
|
if store.cameraAccessGranted {
|
||||||
ZStack {
|
ZStack {
|
||||||
CameraView()
|
CameraView()
|
||||||
.aspectRatio(1, contentMode: .fit)
|
.aspectRatio(1, contentMode: .fit)
|
||||||
|
@ -51,16 +51,7 @@ struct CameraCellPreview: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await checkAuthorization()
|
await store.checkCameraAuthorization()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkAuthorization() async {
|
|
||||||
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
||||||
var isAuthorized = status == .authorized
|
|
||||||
if status == .notDetermined {
|
|
||||||
isAuthorized = await AVCaptureDevice.requestAccess(for: .video)
|
|
||||||
}
|
|
||||||
cameraAuthorized = isAuthorized
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct GalleryView: View {
|
||||||
|
// @State private var selectedItems: [String] = []
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text("test")
|
||||||
|
// Group {
|
||||||
|
// if store.state.sharingState.isGalleryAccessGranted {
|
||||||
|
// ForEach(store.state.sharingState.galleryItems) { item in
|
||||||
|
// GridViewItem(item: item, selected: $selectedItems)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// Button {
|
||||||
|
// openAppSettings()
|
||||||
|
// } label: {
|
||||||
|
// ZStack {
|
||||||
|
// Rectangle()
|
||||||
|
// .fill(Color.Material.Background.light)
|
||||||
|
// .overlay {
|
||||||
|
// VStack {
|
||||||
|
// Image(systemName: "photo")
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// .font(.system(size: 30))
|
||||||
|
// Text("Allow gallery access")
|
||||||
|
// .foregroundColor(.Material.Text.main)
|
||||||
|
// .font(.body3)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .frame(height: 100)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct GridViewItem: View {
|
||||||
|
// let item: SharingGalleryItem
|
||||||
|
@Binding var selected: [String]
|
||||||
|
@State var isSelected = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text("Test")
|
||||||
|
// if let data = item.thumbnail {
|
||||||
|
// ZStack {
|
||||||
|
// Image(uiImage: UIImage(data: data) ?? UIImage())
|
||||||
|
// .resizable()
|
||||||
|
// .aspectRatio(contentMode: .fill)
|
||||||
|
// .frame(width: Const.galleryGridSize, height: Const.galleryGridSize)
|
||||||
|
// .clipped()
|
||||||
|
// if let duration = item.duration {
|
||||||
|
// VStack {
|
||||||
|
// Spacer()
|
||||||
|
// HStack {
|
||||||
|
// Spacer()
|
||||||
|
// Text(duration)
|
||||||
|
// .foregroundColor(.Material.Text.white)
|
||||||
|
// .font(.sub1)
|
||||||
|
// .shadow(color: .black, radius: 2)
|
||||||
|
// .padding(4)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if isSelected {
|
||||||
|
// VStack {
|
||||||
|
// HStack {
|
||||||
|
// Spacer()
|
||||||
|
// Circle()
|
||||||
|
// .frame(width: 30, height: 30)
|
||||||
|
// .shadow(color: .black, radius: 2)
|
||||||
|
// .foregroundColor(.Material.Shape.white)
|
||||||
|
// .overlay {
|
||||||
|
// Image(systemName: "checkmark")
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// .font(.body3)
|
||||||
|
// }
|
||||||
|
// .padding(4)
|
||||||
|
// }
|
||||||
|
// Spacer()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .onTapGesture {
|
||||||
|
// isSelected.toggle()
|
||||||
|
// if isSelected {
|
||||||
|
// selected.append(item.id)
|
||||||
|
// } else {
|
||||||
|
// selected.removeAll { $0 == item.id }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// ZStack {
|
||||||
|
// Rectangle()
|
||||||
|
// .fill(Color.Material.Background.light)
|
||||||
|
// .overlay {
|
||||||
|
// ProgressView()
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// }
|
||||||
|
// .frame(width: Const.galleryGridSize, height: Const.galleryGridSize)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,6 @@ import Photos
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct MediaPickerView: View {
|
struct MediaPickerView: View {
|
||||||
// @State private var showCameraPicker = false
|
|
||||||
// @State private var cameraReady = false
|
|
||||||
|
|
||||||
@State private var selectedItems: [String] = []
|
@State private var selectedItems: [String] = []
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -20,41 +17,9 @@ struct MediaPickerView: View {
|
||||||
CameraCellPreview()
|
CameraCellPreview()
|
||||||
|
|
||||||
// For gallery
|
// For gallery
|
||||||
// if store.state.sharingState.isGalleryAccessGranted {
|
GalleryView()
|
||||||
// ForEach(store.state.sharingState.galleryItems) { item in
|
|
||||||
// GridViewItem(item: item, selected: $selectedItems)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Button {
|
|
||||||
// openAppSettings()
|
|
||||||
// } label: {
|
|
||||||
// ZStack {
|
|
||||||
// Rectangle()
|
|
||||||
// .fill(Color.Material.Background.light)
|
|
||||||
// .overlay {
|
|
||||||
// VStack {
|
|
||||||
// Image(systemName: "photo")
|
|
||||||
// .foregroundColor(.Material.Elements.active)
|
|
||||||
// .font(.system(size: 30))
|
|
||||||
// Text("Allow gallery access")
|
|
||||||
// .foregroundColor(.Material.Text.main)
|
|
||||||
// .font(.body3)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .frame(height: 100)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .fullScreenCover(isPresented: $showCameraPicker) {
|
|
||||||
// CameraPicker(sourceType: .camera) { data, type in
|
|
||||||
// store.dispatch(.sharingAction(.cameraCaptured(media: data, type: type)))
|
|
||||||
// showCameraPicker = false
|
|
||||||
// store.dispatch(.sharingAction(.showSharing(false)))
|
|
||||||
// }
|
|
||||||
// .edgesIgnoringSafeArea(.all)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Send panel
|
// Send panel
|
||||||
Rectangle()
|
Rectangle()
|
||||||
|
@ -79,82 +44,5 @@ struct MediaPickerView: View {
|
||||||
// store.dispatch(.sharingAction(.showSharing(false)))
|
// store.dispatch(.sharingAction(.showSharing(false)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .onAppear {
|
|
||||||
// store.dispatch(.sharingAction(.checkCameraAccess))
|
|
||||||
// store.dispatch(.sharingAction(.checkGalleryAccess))
|
|
||||||
// }
|
|
||||||
// .onChange(of: store.state.sharingState.isGalleryAccessGranted) { granted in
|
|
||||||
// if granted {
|
|
||||||
// store.dispatch(.fileAction(.fetchItemsFromGallery))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct GridViewItem: View {
|
|
||||||
// let item: SharingGalleryItem
|
|
||||||
@Binding var selected: [String]
|
|
||||||
@State var isSelected = false
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Text("Test")
|
|
||||||
// if let data = item.thumbnail {
|
|
||||||
// ZStack {
|
|
||||||
// Image(uiImage: UIImage(data: data) ?? UIImage())
|
|
||||||
// .resizable()
|
|
||||||
// .aspectRatio(contentMode: .fill)
|
|
||||||
// .frame(width: Const.galleryGridSize, height: Const.galleryGridSize)
|
|
||||||
// .clipped()
|
|
||||||
// if let duration = item.duration {
|
|
||||||
// VStack {
|
|
||||||
// Spacer()
|
|
||||||
// HStack {
|
|
||||||
// Spacer()
|
|
||||||
// Text(duration)
|
|
||||||
// .foregroundColor(.Material.Text.white)
|
|
||||||
// .font(.sub1)
|
|
||||||
// .shadow(color: .black, radius: 2)
|
|
||||||
// .padding(4)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if isSelected {
|
|
||||||
// VStack {
|
|
||||||
// HStack {
|
|
||||||
// Spacer()
|
|
||||||
// Circle()
|
|
||||||
// .frame(width: 30, height: 30)
|
|
||||||
// .shadow(color: .black, radius: 2)
|
|
||||||
// .foregroundColor(.Material.Shape.white)
|
|
||||||
// .overlay {
|
|
||||||
// Image(systemName: "checkmark")
|
|
||||||
// .foregroundColor(.Material.Elements.active)
|
|
||||||
// .font(.body3)
|
|
||||||
// }
|
|
||||||
// .padding(4)
|
|
||||||
// }
|
|
||||||
// Spacer()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .onTapGesture {
|
|
||||||
// isSelected.toggle()
|
|
||||||
// if isSelected {
|
|
||||||
// selected.append(item.id)
|
|
||||||
// } else {
|
|
||||||
// selected.removeAll { $0 == item.id }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// ZStack {
|
|
||||||
// Rectangle()
|
|
||||||
// .fill(Color.Material.Background.light)
|
|
||||||
// .overlay {
|
|
||||||
// ProgressView()
|
|
||||||
// .foregroundColor(.Material.Elements.active)
|
|
||||||
// }
|
|
||||||
// .frame(width: Const.galleryGridSize, height: Const.galleryGridSize)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct ConversationTextInput: View {
|
||||||
.tappablePadding(.symmetric(8)) {
|
.tappablePadding(.symmetric(8)) {
|
||||||
router.showScreen(.fullScreenCover) { _ in
|
router.showScreen(.fullScreenCover) { _ in
|
||||||
AttachmentPickerScreen()
|
AttachmentPickerScreen()
|
||||||
|
.environmentObject(conversation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextField("", text: $messageStr, prompt: Text(L10n.Chat.textfieldPrompt).foregroundColor(.Material.Shape.separator))
|
TextField("", text: $messageStr, prompt: Text(L10n.Chat.textfieldPrompt).foregroundColor(.Material.Shape.separator))
|
||||||
|
|
Loading…
Reference in a new issue