mv-experiment #1
|
@ -50,7 +50,16 @@
|
||||||
"Conversation.startError" = "Error occurs in conversation starting";
|
"Conversation.startError" = "Error occurs in conversation starting";
|
||||||
"Chat.textfieldPrompt" = "Type a message";
|
"Chat.textfieldPrompt" = "Type a message";
|
||||||
|
|
||||||
|
// MARK: Attachments
|
||||||
|
"Attachment.Prompt.main" = "Select attachment";
|
||||||
|
"Attachment.Tab.media" = "Media";
|
||||||
|
"Attachment.Tab.files" = "Files";
|
||||||
|
"Attachment.Tab.location" = "Location";
|
||||||
|
"Attachment.Tab.contacts" = "Contacts";
|
||||||
|
"Attachment.Send.media" = "Send media";
|
||||||
|
"Attachment.Send.location" = "Send location";
|
||||||
|
"Attachment.Send.contact" = "Send contact";
|
||||||
|
"Attachment.Downloading.retry" = "Retry";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,13 +73,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Attachments
|
|
||||||
//"Attachment.Prompt.main" = "Select attachment";
|
|
||||||
//"Attachment.Tab.media" = "Media";
|
|
||||||
//"Attachment.Tab.files" = "Files";
|
|
||||||
//"Attachment.Tab.location" = "Location";
|
|
||||||
//"Attachment.Tab.contacts" = "Contacts";
|
|
||||||
//"Attachment.Send.media" = "Send media";
|
|
||||||
//"Attachment.Send.location" = "Send location";
|
|
||||||
//"Attachment.Send.contact" = "Send contact";
|
|
||||||
//"Attachment.Downloading.retry" = "Retry";
|
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum AttachmentTab: Int, CaseIterable {
|
||||||
|
case media
|
||||||
|
case files
|
||||||
|
case location
|
||||||
|
case contacts
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AttachmentPickerScreen: View {
|
||||||
|
@Environment(\.router) var router
|
||||||
|
|
||||||
|
@State private var selectedTab: AttachmentTab = .media
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
// Background color
|
||||||
|
Color.Material.Background.light
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
// Content
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
// Header
|
||||||
|
SharedNavigationBar(
|
||||||
|
leftButton: .init(
|
||||||
|
image: Image(systemName: "xmark"),
|
||||||
|
action: {
|
||||||
|
router.dismissScreen()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
centerText: .init(text: L10n.Attachment.Prompt.main)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pickers
|
||||||
|
switch selectedTab {
|
||||||
|
case .media:
|
||||||
|
MediaPickerView()
|
||||||
|
|
||||||
|
case .files:
|
||||||
|
Color.blue
|
||||||
|
// SharingFilesPickerView()
|
||||||
|
|
||||||
|
case .location:
|
||||||
|
Color.green
|
||||||
|
// SharingLocationPickerView()
|
||||||
|
|
||||||
|
case .contacts:
|
||||||
|
Color.yellow
|
||||||
|
// SharingContactsPickerView()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tab bar
|
||||||
|
AttachmentTabBar(selectedTab: $selectedTab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AttachmentTabBar: View {
|
||||||
|
@Binding var selectedTab: AttachmentTab
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Rectangle()
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: 0.2)
|
||||||
|
.foregroundColor(.Material.Shape.separator)
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
AttachmentTabBarButton(tab: .media, selected: $selectedTab)
|
||||||
|
AttachmentTabBarButton(tab: .files, selected: $selectedTab)
|
||||||
|
AttachmentTabBarButton(tab: .location, selected: $selectedTab)
|
||||||
|
AttachmentTabBarButton(tab: .contacts, selected: $selectedTab)
|
||||||
|
}
|
||||||
|
.background(Color.Material.Background.dark)
|
||||||
|
}
|
||||||
|
.frame(height: 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct AttachmentTabBarButton: View {
|
||||||
|
let tab: AttachmentTab
|
||||||
|
@Binding var selected: AttachmentTab
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
VStack(spacing: 2) {
|
||||||
|
buttonImg
|
||||||
|
.foregroundColor(selected == tab ? .Material.Elements.active : .Material.Elements.inactive)
|
||||||
|
.font(.system(size: 24, weight: .light))
|
||||||
|
.symbolRenderingMode(.hierarchical)
|
||||||
|
Text(buttonTitle)
|
||||||
|
.font(.sub1)
|
||||||
|
.foregroundColor(selected == tab ? .Material.Text.main : .Material.Elements.inactive)
|
||||||
|
}
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(.white.opacity(0.01))
|
||||||
|
.onTapGesture {
|
||||||
|
selected = tab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonImg: Image {
|
||||||
|
switch tab {
|
||||||
|
case .media:
|
||||||
|
return Image(systemName: "photo.on.rectangle.angled")
|
||||||
|
|
||||||
|
case .files:
|
||||||
|
return Image(systemName: "doc.on.doc")
|
||||||
|
|
||||||
|
case .location:
|
||||||
|
return Image(systemName: "location.circle")
|
||||||
|
|
||||||
|
case .contacts:
|
||||||
|
return Image(systemName: "person.crop.circle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonTitle: String {
|
||||||
|
switch tab {
|
||||||
|
case .media:
|
||||||
|
return L10n.Attachment.Tab.media
|
||||||
|
|
||||||
|
case .files:
|
||||||
|
return L10n.Attachment.Tab.files
|
||||||
|
|
||||||
|
case .location:
|
||||||
|
return L10n.Attachment.Tab.location
|
||||||
|
|
||||||
|
case .contacts:
|
||||||
|
return L10n.Attachment.Tab.contacts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
import AVFoundation
|
||||||
|
import MobileCoreServices
|
||||||
|
import Photos
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MediaPickerView: View {
|
||||||
|
// @State private var showCameraPicker = false
|
||||||
|
// @State private var cameraReady = false
|
||||||
|
|
||||||
|
// @State private var selectedItems: [String] = []
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let columns = Array(repeating: GridItem(.flexible(), spacing: 0), count: 3)
|
||||||
|
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
// List of media
|
||||||
|
ScrollView(showsIndicators: false) {
|
||||||
|
LazyVGrid(columns: columns, spacing: 0) {
|
||||||
|
// For camera
|
||||||
|
// if store.state.sharingState.isCameraAccessGranted {
|
||||||
|
// if cameraReady {
|
||||||
|
// ZStack {
|
||||||
|
// CameraView()
|
||||||
|
// .aspectRatio(1, contentMode: .fit)
|
||||||
|
// .frame(maxWidth: .infinity)
|
||||||
|
// Image(systemName: "camera")
|
||||||
|
// .resizable()
|
||||||
|
// .aspectRatio(contentMode: .fit)
|
||||||
|
// .frame(width: 40, height: 40)
|
||||||
|
// .foregroundColor(.white)
|
||||||
|
// .padding(8)
|
||||||
|
// .background(Color.black.opacity(0.5))
|
||||||
|
// .clipShape(Circle())
|
||||||
|
// .padding(8)
|
||||||
|
// }
|
||||||
|
// .onTapGesture {
|
||||||
|
// showCameraPicker = true
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// ProgressView()
|
||||||
|
// .frame(maxWidth: .infinity)
|
||||||
|
// .frame(height: 100)
|
||||||
|
// .onAppear {
|
||||||
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||||
|
// cameraReady = true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// Button {
|
||||||
|
// openAppSettings()
|
||||||
|
// } label: {
|
||||||
|
// ZStack {
|
||||||
|
// Rectangle()
|
||||||
|
// .fill(Color.Material.Background.light)
|
||||||
|
// .overlay {
|
||||||
|
// VStack {
|
||||||
|
// Image(systemName: "camera")
|
||||||
|
// .foregroundColor(.Material.Elements.active)
|
||||||
|
// .font(.system(size: 30))
|
||||||
|
// Text("Allow camera access")
|
||||||
|
// .foregroundColor(.Material.Text.main)
|
||||||
|
// .font(.body3)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .frame(height: 100)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// For gallery
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// .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
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(.Material.Shape.black)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: self.selectedItems.isEmpty ? 0 : 50)
|
||||||
|
.overlay {
|
||||||
|
HStack {
|
||||||
|
Text(L10n.Attachment.Send.media)
|
||||||
|
.foregroundColor(.Material.Text.white)
|
||||||
|
.font(.body1)
|
||||||
|
Image(systemName: "arrow.up.circle")
|
||||||
|
.foregroundColor(.Material.Text.white)
|
||||||
|
.font(.body1)
|
||||||
|
.padding(.leading, 8)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.clipped()
|
||||||
|
.onTapGesture {
|
||||||
|
// store.dispatch(.sharingAction(.shareMedia(ids: selectedItems)))
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class CameraUIView: UIView {
|
||||||
|
// var previewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
//
|
||||||
|
// override func layoutSubviews() {
|
||||||
|
// super.layoutSubviews()
|
||||||
|
// previewLayer?.frame = bounds
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// struct CameraView: UIViewRepresentable {
|
||||||
|
// func makeUIView(context _: Context) -> CameraUIView {
|
||||||
|
// let view = CameraUIView()
|
||||||
|
//
|
||||||
|
// let captureSession = AVCaptureSession()
|
||||||
|
// guard let captureDevice = AVCaptureDevice.default(for: .video) else { return view }
|
||||||
|
// guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return view }
|
||||||
|
// captureSession.addInput(input)
|
||||||
|
//
|
||||||
|
// let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
||||||
|
// previewLayer.videoGravity = .resizeAspectFill
|
||||||
|
// view.layer.addSublayer(previewLayer)
|
||||||
|
// view.previewLayer = previewLayer
|
||||||
|
//
|
||||||
|
// captureSession.startRunning()
|
||||||
|
//
|
||||||
|
// return view
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func updateUIView(_ uiView: CameraUIView, context _: Context) {
|
||||||
|
// uiView.previewLayer?.frame = uiView.bounds
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// struct CameraPicker: UIViewControllerRepresentable {
|
||||||
|
// var sourceType: UIImagePickerController.SourceType
|
||||||
|
// var completionHandler: (Data, SharingCameraMediaType) -> Void
|
||||||
|
//
|
||||||
|
// func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||||
|
// let picker = UIImagePickerController()
|
||||||
|
// picker.sourceType = sourceType
|
||||||
|
// picker.delegate = context.coordinator
|
||||||
|
// picker.mediaTypes = [UTType.movie.identifier, UTType.image.identifier]
|
||||||
|
// picker.videoQuality = .typeHigh
|
||||||
|
// picker.videoMaximumDuration = Const.videoDurationLimit
|
||||||
|
// picker.view.backgroundColor = .clear
|
||||||
|
// return picker
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func updateUIViewController(_: UIImagePickerController, context _: Context) {}
|
||||||
|
//
|
||||||
|
// func makeCoordinator() -> Coordinator {
|
||||||
|
// Coordinator(self)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
|
||||||
|
// let parent: CameraPicker
|
||||||
|
//
|
||||||
|
// init(_ parent: CameraPicker) {
|
||||||
|
// self.parent = parent
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
|
||||||
|
// // swiftlint:disable:next force_cast
|
||||||
|
// let mediaType = info[.mediaType] as! String
|
||||||
|
//
|
||||||
|
// if mediaType == UTType.image.identifier {
|
||||||
|
// if let image = info[.originalImage] as? UIImage {
|
||||||
|
// let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
||||||
|
// parent.completionHandler(data, .photo)
|
||||||
|
// }
|
||||||
|
// } else if mediaType == UTType.movie.identifier {
|
||||||
|
// if let url = info[.mediaURL] as? URL {
|
||||||
|
// let data = try? Data(contentsOf: url)
|
||||||
|
// parent.completionHandler(data ?? Data(), .video)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -2,6 +2,7 @@ import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
struct ConversationTextInput: View {
|
struct ConversationTextInput: View {
|
||||||
|
@Environment(\.router) var router
|
||||||
@EnvironmentObject var conversation: ConversationStore
|
@EnvironmentObject var conversation: ConversationStore
|
||||||
|
|
||||||
@State private var messageStr = ""
|
@State private var messageStr = ""
|
||||||
|
@ -49,7 +50,9 @@ struct ConversationTextInput: View {
|
||||||
.foregroundColor(.Material.Elements.active)
|
.foregroundColor(.Material.Elements.active)
|
||||||
.padding(.leading, 8)
|
.padding(.leading, 8)
|
||||||
.tappablePadding(.symmetric(8)) {
|
.tappablePadding(.symmetric(8)) {
|
||||||
// store.dispatch(.sharingAction(.showSharing(true)))
|
router.showScreen(.fullScreenCover) { _ in
|
||||||
|
AttachmentPickerScreen()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TextField("", text: $messageStr, prompt: Text(L10n.Chat.textfieldPrompt).foregroundColor(.Material.Shape.separator))
|
TextField("", text: $messageStr, prompt: Text(L10n.Chat.textfieldPrompt).foregroundColor(.Material.Shape.separator))
|
||||||
.font(.body1)
|
.font(.body1)
|
||||||
|
|
Loading…
Reference in a new issue