Code Monkey home page Code Monkey logo

chat's Introduction

     

Chat Media Audio Messages Extra

Chat

A SwiftUI Chat UI framework with fully customizable message cells and a built-in media picker

SPM Compatible Cocoapods Compatible Carthage Compatible License: MIT

Features

  • Displays your messages with pagination and allows you to create and "send" new messages (sending means calling a closure since user will be the one providing actual API calls)
  • Allows you to pass a custom view builder for messages and input views
  • Has a built-in photo and video library/camera picker for multiple media asset selection
  • Can display a fullscreen menu on long press a message cell (automatically shows scroll for big messages)
  • Supports "reply to message" via message menu or through a closure. Remove and edit are coming soon
  • This library allows to send the following content in messages in any combination:
    • Text with/without markdown
    • Photo/video
    • Audio recording Coming soon:
    • User's location
    • Documents
    • Link with preview

Usage

Create a chat view like this:

@State var messages: [Message] = []

var body: some View {
    ChatView(messages: messages) { draft in
        yourViewModel.send(draft: draft)
    }
}

where:
messages - list of messages to display
didSendMessage - a closure which is called when the user presses the send button

Message is a type that Chat uses for the internal implementation. In the code above it expects the user to provide a list of Message structs, and it returns a DraftMessage in the didSendMessage closure. You can map it both ways to your own Message model that your API expects or use as is.

Available chat types

Chat type - determines the order of messages and direction of new message animation. Available options:

  • conversation - the latest message is at the bottom, new messages appear from the bottom
  • comments - the latest message is at the top, new messages appear from the top

Reply mode - determines how replying to message looks. Available options:

  • quote - when replying to message A, new message will appear as the newest message, quoting message A in its body
  • answer - when replying to message A, new message with appear direclty below message A as a separate cell without duplicating message A in its body

To specify any of these pass them through init:

ChatView(messages: viewModel.messages, chatType: .comments, replyMode: .answer) { draft in
    yourViewModel.send(draft: draft)
}

Custom UI

You may customize message cells like this:

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} messageBuilder: { message, positionInUserGroup, positionInCommentsGroup, showContextMenuClosure, messageActionClosure, showAttachmentClosure in
    VStack {
        Text(message.text)
        if !message.attachments.isEmpty {
            ForEach(message.attachments, id: \.id) { at in
                AsyncImage(url: at.thumbnail)
            }
        }
    }
}

messageBuilder's parameters:

  • message - the message containing user info, attachments, etc.
  • positionInUserGroup - the position of the message in its continuous collection of messages from the same user
  • positionInCommentsGroup - position of message in its continuous group of comments (only works for .answer ReplyMode, nil for .quote mode)
  • showContextMenuClosure - closure to show message context menu
  • messageActionClosure - closure to pass user interaction, .reply for example
  • showAttachmentClosure - you can pass an attachment to this closure to use ChatView's fullscreen media viewer

You may customize the input view (a text field with buttons at the bottom) like this:

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} inputViewBuilder: { textBinding, attachments, inputViewState, inputViewStyle, inputViewActionClosure, dismissKeyboardClosure in
    Group {
        switch inputViewStyle {
        case .message: // input view on chat screen
            VStack {
                HStack {
                    Button("Send") { inputViewActionClosure(.send) }
                    Button("Attach") { inputViewActionClosure(.photo) }
                }
                TextField("Write your message", text: textBinding)
            }
        case .signature: // input view on photo selection screen
            VStack {
                HStack {
                    Button("Send") { inputViewActionClosure(.send) }
                }
                TextField("Compose a signature for photo", text: textBinding)
                    .background(Color.green)
            }
        }
    }
}

inputViewBuilder's parameters:

  • textBinding to bind your own TextField
  • attachments is a struct containing photos, videos, recordings and a message you are replying to
  • inputViewState - the state of the input view that is controlled by the library automatically if possible or through your calls of inputViewActionClosure
  • inputViewStyle - .message or .signature (the chat screen or the photo selection screen)
  • inputViewActionClosure for calling on taps on your custom buttons. For example, call inputViewActionClosure(.send) if you want to send your message with your own button, then the library will reset the text and attachments and call the didSendMessage sending closure
  • dismissKeyboardClosure - call this to dismiss keyboard

Custom message menu

Long tap on a message will display a menu for this message (can be turned off, see Modifiers). To define custom message menu actions declare an enum conforming to MessageMenuAction. Then the library will show your custom menu options on long tap on message instead of default ones, if you pass your enum's name to it (see code sample). Once the action is selected special callbcak will be called. Here is a simple example:

enum Action: MessageMenuAction {
    case reply, edit

    func title() -> String {
        switch self {
        case .reply:
            "Reply"
        case .edit:
            "Edit"
        }
    }
    
    func icon() -> Image {
        switch self {
        case .reply:
            Image(systemName: "arrowshape.turn.up.left")
        case .edit:
            Image(systemName: "square.and.pencil")
        }
    }
}

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} messageMenuAction: { (action: Action, defaultActionClosure, message) in // <-- here: specify the name of your `MessageMenuAction` enum
    switch action {
    case .reply:
        defaultActionClosure(message, .reply)
    case .edit:
        defaultActionClosure(message, .edit { editedText in
            // update this message's text on your BE
            print(editedText)
        })
    }
}

messageMenuAction's parameters:

  • selectedMenuAction - action selected by the user from the menu. NOTE: when declaring this variable, specify its type (your custom descendant of MessageMenuAction) explicitly
  • defaultActionClosure - a closure taking a case of default implementation of MessageMenuAction which provides simple actions handlers; you call this closure passing the selected message and choosing one of the default actions (.reply, .edit) if you need them; or you can write a custom implementation for all your actions, in that case just ignore this closure
  • message - message for which the menu is displayed

When implementing your own MessageMenuActionClosure, write a switch statement passing through all the cases of your MessageMenuAction, inside each case write your own action handler, or call the default one. NOTE: not all default actions work out of the box - e.g. for .edit you'll still need to provide a closure to save the edited text on your BE. Please see CommentsExampleView in ChatExample project for MessageMenuActionClosure usage example.

Small view builders:

These use AnyView, so please try to keep them easy enough

  • betweenListAndInputViewBuilder - content to display in between the chat list view and the input view
  • mainHeaderBuilder - a header for the whole chat, which will scroll together with all the messages and headers
  • headerBuilder - date section header builder

Modifiers

isListAboveInputView - messages table above the input field view or not
showDateHeaders - show section headers with dates between days, default is true
isScrollEnabled - forbid scrolling for messages' UITabelView
showMessageMenuOnLongPress - turn menu on long tap on/off
showNetworkConnectionProblem - display network error on/off
assetsPickerLimit - set a limit for MediaPicker built into the library
setMediaPickerSelectionParameters - a struct holding MediaPicker selection parameters (assetsPickerLimit and others like mediaType, selectionStyle, etc.).
orientationHandler - handle screen rotation

enableLoadMore(offset: Int, handler: @escaping ChatPaginationClosure) - when user scrolls to offset-th message from the end, call the handler function, so the user can load more messages
chatNavigation(title: String, status: String? = nil, cover: URL? = nil) - pass the info for the Chat's navigation bar

makes sense only for built-in message view

avatarSize - the default avatar is a circle, you can specify its diameter here tapAvatarClosure - closure to call on avatar tap
messageUseMarkdown - use markdown (e.g. ** to make something bold) or not showMessageTimeView - show timestamp in a corner of the message
setMessageFont - pass custom font to use for messages

makes sense only for built-in input view

setAvailableInput - hide some buttons in default InputView. Available options are: - .full - media + text + audio
- .textAndMedia
- .textAndAudio
- .textOnly

Examples

There are 2 example projects:

  • One has a simple bot posting random text/media messages every 2 seconds. It has no back end and no local storage. Every new start is clean and fresh.
  • Another has an integration with Firestore data base. It has all the necessary back end support, including storing media and audio messages, unread messages counters, etc. You'll have to create your own Firestore app and DB. Also replace GoogleService-Info with your own. After that you can test on multiple sims/devices.

Create your firestore app https://console.firebase.google.com/ Create firesote database (for light weight text data) https://firebase.google.com/docs/firestore/manage-data/add-data Create cloud firestore database (for images and voice recordings) https://firebase.google.com/docs/storage/web/start

Example

To try out the Chat examples:

  • Clone the repo git clone [email protected]:exyte/Chat.git
  • Open terminal and run cd <ChatRepo>/Example
  • Wait for SPM to finish downloading packages
  • Run it!

Installation

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/exyte/Chat.git")
]

CocoaPods

pod 'ExyteChat'

Carthage

github "Exyte/Chat"

Requirements

  • iOS 16+
  • Xcode 14+

Our other open source SwiftUI libraries

PopupView - Toasts and popups library
Grid - The most powerful Grid container
ScalingHeaderScrollView - A scroll view with a sticky header which shrinks as you scroll
AnimatedTabBar - A tabbar with a number of preset animations
MediaPicker - Customizable media picker
OpenAI Wrapper lib for OpenAI REST API
AnimatedGradient - Animated linear gradient
ConcentricOnboarding - Animated onboarding flow
FloatingButton - Floating button menu
ActivityIndicatorView - A number of animated loading indicators
ProgressIndicatorView - A number of animated progress indicators
FlagAndCountryCode - Phone codes and flags for every country
SVGView - SVG parser
LiquidSwipe - Liquid navigation animation

chat's People

Contributors

alex-m-b95 avatar alexandra-afonasova avatar f3dm76 avatar jackyoustra avatar juroberttyb avatar kgolenkov avatar mojtabahs avatar nikita-afonasov avatar rnewman avatar urvashi-housing avatar villygreen avatar zapletnev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chat's Issues

Get a crash every time I append a new message

I get the error below every time I append messages

@Observable
class AblyServiceManager  {
    var allUsers: Set<User> = []
    var messages: [Message] = []

func addNewMSG {
messages.append(newMSG)
}
}
*** Assertion failure in -[UITableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:], UITableView.m:2,611
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). Table view: <UITableView: 0x10b11e000; frame = (0 0; 430 718); transform = [-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0]; clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000cf7510>; backgroundColor = UIExtendedSRGBColorSpace 1 1 1 1; layer = <CALayer: 0x6000003248c0>; contentOffset: {0, 0}; contentSize: {429.99999999999989, 103.43333206176757}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <_TtGCV9ExyteChat6UIList11CoordinatorV7SwiftUI9EmptyView__: 0x60000330b2a0>>'
*** First throw call stack:

Dismiss chat with swipe from left side of screen

Most pushed views allow swiping from the left side of the screen to pop them back to the previous view. This is especially helpful for chat views, where most of the screen is covered by the keyboard and the top-left back button is out of reach. It would be nice to have the option for chat views to be dismissed with this gesture.

I believe this is currently disabled due to the .navigationBarBackButtonHidden() modifier on the ChatNavigationModifier. A simple solution would be another field in that struct to allow the system back button instead of the themed one.

Thank you for your consideration!

Reply Menu Issues

  1. the Reply btn on long press is hidden by the message that is long pressed when the message is >2 lines long
  2. tapping the Reply btn doesnt actually change anything in the UI that I can tell.

Is there a way to turn off the long press?

Message update, UI interface does not refresh issue.

I am interfacing with OpenAI's SSE format API, and I tried to update the content of corresponding messages. It can be confirmed that the message has been updated, but I did not see the update in the UI. Is there a problem with the way I call it?thx
My code

    private func updateBotMessage(_ text: String, messageId: String?) {
        DispatchQueue.main.async {
            if let currentId = self.currentBotMessageId,
               let index = self.messages.firstIndex(where: { $0.id == currentId }) {
                var updatedMessage = self.messages[index]
                updatedMessage.text += text
                self.messages[index] = updatedMessage
            } else {
                let newId = messageId ?? UUID().uuidString
                let newMessage = Message(
                    id: newId,
                    user: self.botUser,
                    status: .sent,
                    createdAt: Date(),
                    text: text
                )
                self.messages.append(newMessage)
                self.currentBotMessageId = newId
            }
            
            self.objectWillChange.send()
            print("Current bot message: \(self.messages.last?.text ?? "")")
        }
    }

Log

EventSource connection opened
Received event: message
Current bot message: Hello
Received event: message
Received event: message
Current bot message: Hello!
Current bot message: Hello! How
Received event: message
Received event: message
Current bot message: Hello! How can
Current bot message: Hello! How can I
Received event: message
Current bot message: Hello! How can I assist
Received event: message
Current bot message: Hello! How can I assist you
Received event: message
Current bot message: Hello! How can I assist you today
Received event: message
Current bot message: Hello! How can I assist you today?
Received event: message
Received event: message
Current bot message: Hello! How can I assist you today?

Internal Inconsistency crash

Hi

I'm seeing occurrences of the NSInternalInconsistencyException crash, that seems to have been an issue for other users in the past based on closed issues:

Fatal Exception: NSInternalInconsistencyException Invalid batch updates detected: the number of sections and/or rows returned by the data source before and after performing the batch updates are inconsistent with the updates. Data source before updates = { 1 section with row counts: [3] } Data source after updates = { 1 section with row counts: [0] } Updates = [ Delete row (0 - 1), Delete row (0 - 0) ]

Crash coming from UIList.swift, line 104 as it attempts to performBatchUpdates.

I've not been able to replicate in testing as yet, but getting regular reports of the crash from our production release.

Any advice welcome... Thanks.

Chat.framework is missing

I'm using SPM inside a XCode project. I added this project and I keep getting this error

CrashReportError: App crashed because Chat.framework is missing

Not sure what is causing this, any particular idea?

Seeking Advice: How to Add 'message response buttons' to a message

I'm seeking some advice regarding how I should go about adding message response buttons like we see in WhatsApp and other messaging platforms. They display convenient short answer options in a chat message as shown in the attachment. Any suggestions where I should start for implementing this capability? There can be limitations, such as a max of 3 buttons, and pressing the button only fills in the draft message and automatically sends the message. I would appreciate any thoughts about where to get started and which classes should be extended.

message-buttons

How to build a first sample message

Hello
I am trying to use exyte chat for the first time but without success. In the most simple code:

@State var messages: [Message] = []

var body: some View {
ChatView(messages: messages) { draft in
//yourViewModel.send(draft: draft)
}
}

how can I initialize the messages array with a first sample message to be displayed when the screen first appears?
I am trying to use onAppear() to just append a simple and sample message but it is being hard to initialize it. Could you provide what to pass the the append call?

Thank you

Exyte Chat Crashing After 'DidSendMessage'

Summary

Exyte Chat will throw an exemption after sending a message through the chat input text field.

Source of the Issue

The following exemption is thrown, and it is caused due to the TableView not having any cells to for which to render after NotificationCenter.default.post(name: .onScrollToBottom, object: nil) was called upon.

Attempted to scroll the table view to an out-of-bounds section (0) when there are only 0 sections

Notes

I noticed that Exyte Chat is using Notification Center to send .scrollToBottom event, and there is a timeout of 0.3.
I wonder if there is a race condition happening here in which the data source is not updated on time for that scroll event to call the table view.

Dark Mode?

is there support for dark mode without having to create a theme on my own. Maybe this should be an out to the box feature.

dynamic font size for messages

Love this repo. Impressive work.

How would i add dynamic font size for messages for people with bad eyes?

My users are mostly >50 years old. I see that font size of 15 is hardcoded on MessageView.swift line36. I love everything else about the MessageView, and definitely want to avoid implementing a custom messageBuilder if i can help it.

iOS 15 support

Amazing Library, thanks you very much
would you guys make it compatible with iOS 15?
as the required version is too new to make the usage worldwide

Exyte Chat Crashing When Delete Row or Rows

Hi developer, Thank you for creating an excellent chat library In SwiftUI.

when I tried to delete the item at binding dataSource source, will crash immediately at performBatchUpdates in UIList, below is the log:
Thread 1: "Invalid batch updates detected: the number of sections and/or rows returned by the data source before and/or after performing the batch updates are inconsistent with the updates.\nData source before updates = { 1 section with row counts: [3] }\nData source after updates = { 1 section with row counts: [2] }\nUpdates = [\n\n]\nTable view: <UITableView: 0x125850a00;

I check the code, and the applyInserts function already exists to deal with delete cases.

Allow customizing the media picker mode

Hi! First of all, thanks for open-sourcing this package!

I would like to customize the media types selectable in the media picker (only photos / only videos / both).
I am aware that ExyteMediaPicker allows defining a mediaSelectionType. However, if I understand correctly, ExyteChat only exposes the mediaSelectionLimit property of the underlying ExyteMediaPicker via an assetsPickerLimit modifier; the mediaSelectionType property is not exposed.

Is there a way for me to customize the media selection type directly from ExyteChat?
Thank you!

Load more Crash

After adding new items into @Published var messages: [Message] = [] get a crash :(
When add only 1 item it works fine.

*** Assertion failure in -[UITableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:], UITableView.m:2 611 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (40) must be equal to the number of rows contained in that section before the update (20), plus or minus the number of rows inserted or deleted from that section (12 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). Table view: <UITableView: 0x11a12ee00; frame = (-3.12915e-14 0; 393 662.333); transform = [-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0]; clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000d573f0>; backgroundColor = UIExtendedSRGBColorSpace 1 1 1 1; layer = <CALayer: 0x600000324c00>; contentOffset: {0, 985.66666666666663}; contentSize: {392.99999999999994, 1648.1000495910644}; adjustedContentInset: {1.4210854715202004e-14, 0, 0, 0}; dataSource: <_TtGCV9ExyteChat6UIList11CoordinatorV8Futurest15ChatMessageView__: 0x60000330b0c0>>' *** First throw call stack: ( 0 CoreFoundation 0x00000001804658a8 __exceptionPreprocess + 172 1 libobjc.A.dylib 0x000000018005c09c objc_exception_throw + 56 2 Foundation 0x0000000180cf3a5c _userInfoForFileAndLine + 0 3 UIKitCore 0x000000011522f8f0 -[UITableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:] + 92 4 UIKitCore 0x000000011522f14c -[UITableView _endCellAnimationsWithContext:] + 9676 5 UIKitCore 0x00000001152430b0 -[UITableView endUpdatesWithContext:] + 128 6 Futurest 0x00000001014112ac $s9ExyteChat6UIListV12updateUIView_7contextySo11UITableViewC_7SwiftUI0E20RepresentableContextVyACyxGGtFyyYbcfU_yyXEfU2_ + 1000 7 Futurest 0x000000010140fcec $sIg_Ieg_TR + 20 8 Futurest 0x000000010140fd10 $sIeg_IyB_TR + 24 9 libdispatch.dylib 0x00000001045cd93c _dispatch_client_callout + 16 10 libdispatch.dylib 0x00000001045de80c _dispatch_async_and_wait_invoke + 192 11 libdispatch.dylib 0x00000001045cd93c _dispatch_client_callout + 16 12 libdispatch.dylib 0x00000001045dd5e4 _dispatch_main_queue_drain + 1228 13 libdispatch.dylib 0x00000001045dd108 _dispatch_main_queue_callback_4CF + 40 14 CoreFoundation 0x00000001803c61b4 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 15 CoreFoundation 0x00000001803c08cc __CFRunLoopRun + 1936 16 CoreFoundation 0x00000001803bfd28 CFRunLoopRunSpecific + 572 17 GraphicsServices 0x000000018986ebc0 GSEventRunModal + 160 18 UIKitCore 0x0000000115033fdc -[UIApplication _run] + 868 19 UIKitCore 0x0000000115037c54 UIApplicationMain + 124 20 SwiftUI 0x000000010ba40524 OUTLINED_FUNCTION_70 + 500 21 SwiftUI 0x000000010ba403c4 OUTLINED_FUNCTION_70 + 148 22 SwiftUI 0x000000010b752108 OUTLINED_FUNCTION_2 + 92 23 Futurest 0x0000000100fd5bc8 $s8Futurest0A3AppV5$mainyyFZ + 40 24 Futurest 0x0000000100fd6040 main + 12 25 dyld 0x0000000103451558 start_sim + 20 26 ??? 0x0000000102f52058 0x0 + 4344586328 27 ??? 0x0215000000000000 0x0 + 150026162586779648 ) libc++abi: terminating due to uncaught exception of type NSException

How can we add custom message type?

I want to support custom message types. For example, I have documents that I want to send as a message when they are created. The message should contain information such as: title, blurb, documentId. How can I store this inside of a Message?

I would also want to provide custom styling, which I can assume can be done through messageBuilder. I am wondering how we can structure the Message so that we can switch on it inside the messageBuilder and display accordingly.

I want to keep all the displays of the standard messages the same other than my custom message type.

Any advice is greatly appreciated!

Development

Design:
https://www.figma.com/file/mtMqPG1bTFKNMBW3pgBlax/Open-source-library-for-Exyte?node-id=606%3A2
Вопросы по дизайну направлять Юле, вопросы по фичам в канал ios-opensource

Баги:

  • Анимация появления сообщения в чате какая-то дерганая, и все фотки мигают каждое новое сообщение почему-то - надо сделать плавную анимацию

Фичи (когда-то потом):

  • Добавить плавающий хедер даты сверху чата (today, yesterday и все такое)
  • Добавить контекстное меню:
    • "edit, delete" для своих сообщений
    • "retry, edit, delete" для своих сообщений которые зафейлились послаться
    • "reply" для чужих (тут надо будет еще в строку ввода добавить превью сообщения, на которое ты отвечаешь)
  • Добавить возможность тегать по имени
  • Разрешить отправлять локацию

Custom InputView dismissing keyboard when chat view is tapped

Using the example to build my own custom input view, I'm having a hard time understanding how I would dismiss the keyboard when the user taps on the chat view

I notice the internal implementation of the ChatView has

 @StateObject private var globalFocusState = GlobalFocusState()
.environmentObject(globalFocusState)

but that is not something I can access inside inputViewBuilder. I tried defining my own

@FocusState private var isChatFocus: Bool

but i don't have a handler to use to dismiss it when the user taps on the chat view. I tried onTapGesture on the chat view, but it doesn't fire.

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} inputViewBuilder: { textBinding, attachments, state, style, actionClosure in
    Group {
        switch style {
        case .message: // input view on chat screen
            VStack {
                HStack {
                    Button("Send") { actionClosure(.send) }
                    Button("Attach") { actionClosure(.photo) }
                }
                TextField("Write your message", text: textBinding)
            }
        case .signature: // input view on photo selection screen
            VStack {
                HStack {
                    Button("Send") { actionClosure(.send) }
                }
                TextField("Compose a signature for photo", text: textBinding)
                    .background(Color.green)
            }
        }
    }
}```

Crash when launching chat due to lack of NSPhotoLibraryUsageDescription

Is there a way to only allow text as a message type? My app doesn't intend to send photos and I'd prefer not to fill out a NSPhotoLibraryUsageDescription since it really isn't needed.

As a suggestion - it may be helpful to first time users to outline what Privacy descriptions they may need so they can avoid the crash as well.

CameraStubView not closed when tapped on "Close" button

  1. Run the app in the Simulator.
  2. Tap the Camera button in text input view
  3. CameraStubView is presented with Close button
  4. Tap the Close button. Nothing happens.

Expected: CameraStubView should be closed when tapped on Close button

Swift Errors in MessageMenu.swift

After updating to 1.2.2 from 1.0.3, I'm getting Errors during compile in the latest Xcode 15.0.1 with iOS 17:

Screenshot 2023-11-06 at 23 32 23 Screenshot 2023-11-06 at 23 32 30

This is the code snippet in MessagesMenu.swift > body:

    var body: some View {
        FloatingButton(mainButtonView: mainButton().allowsHitTesting(false), buttons: [
            menuButton(title: "Reply", icon: theme.images.messageMenu.reply, action: .reply)
        ], isOpen: $isShowingMenu)
        .straight()
        .mainZStackAlignment(.top)
        .initialOpacity(0)
        .direction(.bottom)
        .alignment(alignment)
        .spacing(2)
        .animation(.linear(duration: 0.2))
        .menuButtonsSize($menuButtonsSize)
    }

Link to code in GH repo: https://github.com/exyte/Chat/blob/main/Sources/ExyteChat/ChatView/MessageView/MessageMenu.swift#L33

I tried resetting the SPM package cache and cleaning the project but the issue persists. Any ideas?

Notifications?

Does this natively support push notifications on new messages?

Why using UITableView instead of List

Hello,

I was looking at your library and I noticed you were using a UITableView in a UIList.swift instead of the List component.

Why did you do that ? What was the limitation ?

Thanks :)

[Request] Feature Suggestions and Bug Discovery

I have been using the source code you created, and it's very well. Below, I have detailed what I've noticed while working with it.

PS: I translated this because I'm not proficient in English. If there are any parts you don't understand, please answer.

Working Environment:

  • XCode: Version 14.3.1 (14E300c)
  • Swift: Version 5
  • iOS: 15.0 or higher

Feature Suggestions:

1. Add Menu on Message's Long Press Gesture:

  • Copy message
  • Edit message
  • Delete message
  • Forward
  • Message's Long Press Gesture:

2. Add shadow to message Bubble

3. Detailed View of Image Messages:

  • download image
  • Copy image

4. Voice Message Recording:
- Provide voice spectrum instead of displaying "Recording..."
ex) https://volpato.dev/posts/speech-wave-visualization-in-swiftui/

5. When New Messages Arrive (with isScrolledToBottom = true):
- Display the number of new messages as a badge

6. Add Style to Buttons:
- Use .buttonStyle(.plain)

7. Add a Closure for Handling Message Display Animations

8. Feature to Change the Profile in the Message Window When the User Profile Changes

Things Suspected to be Bugs:

  • Sometimes, the layout overlaps when displaying messages.
  • The chat scroll animation operates abnormally, causing a shaking problem.
  • The scroll animation behaves unpredictably when there are only a few messages (about 1-3).

Feature request: 'user is typing' message bubble

Is it possible to add a 'user is typing' feature. Something like a message bubble with three dots and list of chat-users in the caption-label bellow the buble, indicating who is currently typing in the chat?

Can't load any icons when using cocoapod

I am trying this repo with cocoapod for my project, however, there is no icon loaded in the chatview, I also double check the pod source and it seems the media resource was missing. Did we miss that on pod repo or I miss something? Thank you.

Newlines in Markdown attributed strings don't work

The code here uses NSAttributedString for rendering markdown.

Unfortunately I'm seeing this render incorrectly: \n in the input string does not result in a newline (or any whitespace at all) in the rendered output. ** and similar text instructions do work.

This seems like an old issue:

https://forums.developer.apple.com/forums/thread/682957

A potential workaround is to just dump the string straight into the Text.

If I get a chance this week I'll try to test this out myself, but I figured I'd file an issue first in case you beat me to it!

Feature request: use environments instead of vars

Now all chat configuration like showDateHeaders, messageUseMarkdown, avatarSize, etc, are simple variables and configured through methods from ChatView extension. It means, that it's possible to set them only locally, directly with a ChatView. I suggest to use EnvironmentValues instead that allows to setup all configs globally for the whole app and override them locally if needed.
Example:

extension EnvironmentValues {
   
   public struct Chat: Equatable {
     public var type: ChatType = .chat
     public var showDateHeaders: Bool = true
     public var avatarSize: CGFloat = 32
     public var messageUseMarkdown: Bool = false
     public var showMessageMenuOnLongPress: Bool = true
     public var tapAvatarClosure: TapAvatarClosure?
     public var mediaPickerSelectionParameters: MediaPickerParameters?
     public var orientationHandler: MediaPickerOrientationHandler = {_ in}
     public var chatTitle: String?
     public var showMessageTimeView = true
     public var messageFont = UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 15))
     public var availablelInput: AvailableInputType = .full
   }
   
   private enum ChatKey: EnvironmentKey {
     static let defaultValue = EnvironmentValues.Chat()
   }
   
   public var chat: Chat {
      get { self[ChatKey.self] }
      set { self[ChatKey.self] = newValue }
   }
}

extension View {

    public func chatView<T>(_ keyPath: WritableKeyPath<EnvironmentValues.Chat, T>, _ value: T) -> some View {
       environment(\.chat[keyPath: keyPath], value)
    }
    
    // convenience methods for each var if needed
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.