2024-06-19 15:15:27 +00:00
import Combine
import Foundation
import Martin
import SwiftUI
2024-06-21 10:42:50 +00:00
struct ConversationScreen : View {
@ EnvironmentObject var store : AppStore
2024-06-19 15:15:27 +00:00
var body : some View {
2024-06-24 13:28:26 +00:00
ZStack {
// B a c k g r o u n d c o l o r
Color . Main . backgroundLight
. ignoresSafeArea ( )
2024-06-19 15:15:27 +00:00
2024-06-24 13:28:26 +00:00
// C o n t e n t
VStack ( spacing : 0 ) {
// H e a d e r
ConversationScreenHeader ( )
// M s g l i s t
let messages = store . state . conversationsState . currentMessages
if ! messages . isEmpty {
List {
ForEach ( messages ) { message in
ConversationMessageRow ( message : message )
}
}
. listStyle ( . plain )
. background ( Color . Main . backgroundLight )
2024-06-26 07:27:23 +00:00
. scrollDismissesKeyboard ( . immediately )
2024-06-24 13:28:26 +00:00
} else {
Spacer ( )
}
}
2024-06-26 07:27:23 +00:00
. onTapGesture {
UIApplication . shared . sendAction ( #selector ( UIResponder . resignFirstResponder ) , to : nil , from : nil , for : nil )
}
2024-06-19 15:15:27 +00:00
}
2024-06-25 11:13:59 +00:00
. safeAreaInset ( edge : . bottom , spacing : 0 ) {
ConversationScreenTextInput ( )
}
}
}
struct ConversationScreenTextInput : View {
@ EnvironmentObject var store : AppStore
@ State private var messageStr = " "
var body : some View {
HStack {
Image ( systemName : " paperclip " )
. font ( . title2 )
. foregroundColor ( . Tango . blueLight )
. padding ( . leading , 8 )
. tappablePadding ( . symmetric ( 8 ) ) {
print ( " Attach file " )
}
TextField ( L10n . Chat . textfieldPrompt , text : $ messageStr )
. font ( . body1 )
. padding ( . horizontal , 8 )
. padding ( . vertical , 4 )
. background ( Color . Main . white )
. clipShape ( RoundedRectangle ( cornerRadius : 8 ) )
. padding ( . vertical , 4 )
let img = messageStr . isEmpty ? " paperplane " : " paperplane.fill "
Image ( systemName : img )
. font ( . title2 )
. foregroundColor ( messageStr . isEmpty ? . Main . separator : . Tango . blueLight )
. padding ( . trailing , 8 )
. tappablePadding ( . symmetric ( 8 ) ) {
if ! messageStr . isEmpty {
2024-06-26 08:00:59 +00:00
guard let acc = store . state . conversationsState . currentChat ? . account else { return }
guard let contact = store . state . conversationsState . currentChat ? . participant else { return }
store . dispatch ( . conversationAction ( . sendMessage (
from : acc ,
to : contact ,
body : messageStr
) ) )
2024-06-25 11:13:59 +00:00
messageStr = " "
UIApplication . shared . sendAction ( #selector ( UIResponder . resignFirstResponder ) , to : nil , from : nil , for : nil )
}
}
}
. padding ( . vertical , 8 )
. background ( Color . Main . backgroundDark )
2024-06-19 15:15:27 +00:00
}
}
2024-06-21 10:42:50 +00:00
private struct ConversationScreenHeader : View {
2024-06-26 06:51:56 +00:00
@ EnvironmentObject var store : AppStore
2024-06-19 15:15:27 +00:00
var body : some View {
ZStack {
// b g
Color . Main . backgroundDark
. ignoresSafeArea ( )
// t i t l e
2024-06-26 06:51:56 +00:00
let name = (
store . state . conversationsState . currentRoster ? . name ? ?
store . state . conversationsState . currentRoster ? . contactBareJid
) ? ? L10n . Chat . title
Text ( name )
2024-06-19 15:15:27 +00:00
. font ( . head2 )
. foregroundColor ( Color . Main . black )
HStack {
Image ( systemName : " chevron.left " )
. foregroundColor ( Color . Tango . orangeMedium )
. tappablePadding ( . symmetric ( 12 ) ) {
2024-06-21 10:42:50 +00:00
store . dispatch ( . changeFlow ( store . state . previousFlow ) )
2024-06-19 15:15:27 +00:00
}
Spacer ( )
}
. padding ( . horizontal , 16 )
}
. frame ( height : 44 )
}
}
2024-06-24 13:28:26 +00:00
private struct ConversationMessageRow : View {
@ EnvironmentObject var store : AppStore
2024-06-19 15:15:27 +00:00
let message : Message
2024-06-27 07:23:09 +00:00
@ State private var offset : CGSize = . zero
2024-06-19 15:15:27 +00:00
var body : some View {
2024-06-25 11:13:59 +00:00
VStack ( spacing : 0 ) {
HStack ( spacing : 0 ) {
if isOutgoing ( ) {
2024-06-24 13:28:26 +00:00
Spacer ( )
2024-06-26 10:26:04 +00:00
MessageAttr ( message : message )
. padding ( . trailing , 4 )
2024-06-24 13:28:26 +00:00
}
2024-06-25 11:13:59 +00:00
MessageContainer ( message : message , isOutgoing : isOutgoing ( ) )
. background ( isOutgoing ( ) ? Color . Material . greenDark100 : Color . Main . white )
. clipShape ( MessageBubble ( isOutgoing : isOutgoing ( ) ) )
if ! isOutgoing ( ) {
2024-06-26 10:26:04 +00:00
MessageAttr ( message : message )
. padding ( . leading , 4 )
2024-06-24 13:28:26 +00:00
Spacer ( )
}
}
2024-06-25 11:13:59 +00:00
. padding ( . vertical , 10 )
. padding ( . horizontal , 16 )
2024-06-27 07:23:09 +00:00
. background ( Color . clearTappable )
. offset ( offset )
. gesture (
DragGesture ( minimumDistance : 20 , coordinateSpace : . local )
. onEnded { value in
withAnimation ( . easeOut ( duration : 0.1 ) ) {
if value . translation . width < 0 {
offset = CGSize ( width : - 50 , height : 0 )
// TODO: Q u i c k m e s s a g e r e p l a y h e r e
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.2 ) {
withAnimation ( . easeOut ( duration : 0.1 ) ) {
offset = . zero
}
}
} else {
offset = . zero
}
}
}
)
2024-06-19 15:15:27 +00:00
}
2024-06-24 13:28:26 +00:00
. sharedListRow ( )
2024-06-19 15:15:27 +00:00
}
2024-06-25 11:13:59 +00:00
private func isOutgoing ( ) -> Bool {
message . from = = store . state . conversationsState . currentChat ? . account
}
}
struct MessageContainer : View {
let message : Message
let isOutgoing : Bool
var body : some View {
Text ( message . body ? ? " ... " )
. font ( . body2 )
. foregroundColor ( Color . Main . black )
. multilineTextAlignment ( . leading )
. padding ( 10 )
}
}
struct MessageBubble : Shape {
let isOutgoing : Bool
func path ( in rect : CGRect ) -> Path {
let path = UIBezierPath (
roundedRect : rect ,
byRoundingCorners : isOutgoing ? [ . topLeft , . bottomLeft , . bottomRight ] : [ . topRight , . bottomLeft , . bottomRight ] ,
cornerRadii : CGSize ( width : 8 , height : 10 )
)
return Path ( path . cgPath )
2024-06-24 13:28:26 +00:00
}
2024-06-19 15:15:27 +00:00
}
2024-06-25 11:13:59 +00:00
2024-06-26 10:26:04 +00:00
struct MessageAttr : View {
2024-06-25 11:13:59 +00:00
let message : Message
var body : some View {
2024-06-26 10:26:04 +00:00
VStack ( alignment : . leading , spacing : 0 ) {
Text ( message . date , style : . time )
. font ( . sub2 )
. foregroundColor ( Color . Main . gray )
Spacer ( )
if message . sentError {
Image ( systemName : " exclamationmark.circle " )
. font ( . body3 )
. foregroundColor ( Color . Tango . redLight )
} else if message . pending {
Image ( systemName : " clock " )
. font ( . body3 )
. foregroundColor ( Color . Main . gray )
}
}
2024-06-25 11:13:59 +00:00
}
}
2024-06-27 07:23:09 +00:00
// s e l f . c o n v e r s a t i o n L o g C o n t r o l l e r ? . g e t T e x t O f S e l e c t e d R o w s ( p a t h s : [ i n d e x P a t h ] , w i t h T i m e s t a m p s : f a l s e , h a n d l e r : { [ w e a k s e l f ] t e x t s i n
// l e t t e x t : S t r i n g = t e x t s . f l a t M a p { $ 0 . s p l i t ( s e p a r a t o r : " \ n " ) } . m a p {
// i f $ 0 . s t a r t s ( w i t h : " > " ) {
// r e t u r n " > \ ( $ 0 ) " ;
// } e l s e {
// r e t u r n " > \ ( $ 0 ) "
// }
// } . j o i n e d ( s e p a r a t o r : " \ n " ) ;
//
// i f l e t c u r r e n t = s e l f ? . m e s s a g e T e x t , ! c u r r e n t . i s E m p t y {
// s e l f ? . m e s s a g e T e x t = " \ ( c u r r e n t ) \ n \ ( t e x t ) \ n " ;
// } e l s e {
// s e l f ? . m e s s a g e T e x t = " \ ( t e x t ) \ n " ;
// }
// } )
2024-06-25 11:13:59 +00:00
// P r e v i e w
#if DEBUG
struct ConversationScreen_Previews : PreviewProvider {
static var previews : some View {
ConversationScreen ( )
. environmentObject ( pStore )
}
static var pStore : AppStore {
let state = pState
return AppStore ( initialState : state , reducer : AppState . reducer , middlewares : [ ] )
}
static var pState : AppState {
var state = AppState ( )
let acc = " user@test.com "
let contact = " some@test.com "
state . conversationsState . currentChat = Chat ( id : " 1 " , account : acc , participant : contact , type : . chat )
state . conversationsState . currentMessages = [
Message (
id : " 1 " ,
type : . chat ,
contentType : . text ,
from : contact ,
to : acc ,
body : " this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf " ,
subject : nil ,
thread : nil ,
oobUrl : nil ,
2024-06-26 07:27:23 +00:00
date : Date ( ) ,
2024-06-26 10:26:04 +00:00
pending : true , sentError : false
2024-06-25 11:13:59 +00:00
) ,
2024-06-27 07:23:09 +00:00
Message (
id : " 2 " ,
type : . chat ,
contentType : . text ,
from : contact ,
to : acc ,
body : " this is for testsdfsdf sdfsdf sdfs sdf sdffsdf sdf sdf sdf sdf sdf sdff sdfffwwe " ,
subject : nil ,
thread : nil ,
oobUrl : nil ,
date : Date ( ) ,
pending : false ,
sentError : false
) ,
2024-06-26 10:26:04 +00:00
Message ( id : " 3 " , type : . chat , contentType : . text , from : contact , to : acc , body : " this is for test " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : true ) ,
2024-06-27 07:23:09 +00:00
Message (
id : " 4 " ,
type : . chat ,
contentType : . text ,
from : acc ,
to : contact ,
body : " this is for test sdfkjwek jwkjfh jwerf jdfhskjdhf jsdhfjhwefh sjdhfh fsdjhfh sd " ,
subject : nil ,
thread : nil ,
oobUrl : nil ,
date : Date ( ) ,
pending : false ,
sentError : false
) ,
2024-06-26 10:26:04 +00:00
Message ( id : " 5 " , type : . chat , contentType : . text , from : contact , to : acc , body : " this is for test sdfjkkeke kekkddjw;; w;edkdjfj l kjwekrjfk wef " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false ) ,
Message ( id : " 6 " , type : . chat , contentType : . text , from : acc , to : contact , body : " this is for testsdf dsdkkekkddn wejkjfj " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false ) ,
2024-06-25 11:13:59 +00:00
Message (
id : " 7 " ,
type : . chat ,
contentType : . text ,
from : acc ,
to : contact ,
body : " this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf " ,
subject : nil ,
thread : nil ,
oobUrl : nil ,
2024-06-26 10:26:04 +00:00
date : Date ( ) , pending : false , sentError : false
2024-06-25 11:13:59 +00:00
) ,
2024-06-26 10:26:04 +00:00
Message ( id : " 8 " , type : . chat , contentType : . text , from : acc , to : contact , body : " so test " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false ) ,
Message ( id : " 9 " , type : . chat , contentType : . text , from : contact , to : acc , body : " so test " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false ) ,
Message ( id : " 10 " , type : . chat , contentType : . text , from : acc , to : contact , body : " so test so test so test " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false ) ,
Message ( id : " 11 " , type : . chat , contentType : . text , from : contact , to : acc , body : " xD " , subject : nil , thread : nil , oobUrl : nil , date : Date ( ) , pending : false , sentError : false )
2024-06-25 11:13:59 +00:00
]
return state
}
}
#endif