Skip to content

Commit dd63075

Browse files
implemented mute command
1 parent 015b4b8 commit dd63075

File tree

8 files changed

+314
-25
lines changed

8 files changed

+314
-25
lines changed

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,19 @@ public class MessageComposerViewModel: ObservableObject {
148148
editedMessage: ChatMessage?,
149149
completion: @escaping () -> Void
150150
) {
151+
if let composerCommand = composerCommand {
152+
commandsHandler.executeOnMessageSent(
153+
composerCommand: composerCommand
154+
) { [weak self] _ in
155+
self?.clearInputData()
156+
completion()
157+
}
158+
159+
if composerCommand.replacesMessageSent {
160+
return
161+
}
162+
}
163+
151164
if let editedMessage = editedMessage {
152165
edit(message: editedMessage, completion: completion)
153166
return
@@ -206,7 +219,13 @@ public class MessageComposerViewModel: ObservableObject {
206219
}
207220

208221
public var sendButtonEnabled: Bool {
209-
!addedAssets.isEmpty ||
222+
if let composerCommand = composerCommand,
223+
let handler = commandsHandler.canShowSuggestions(for: composerCommand) {
224+
return handler
225+
.canBeExecuted(composerCommand: composerCommand)
226+
}
227+
228+
return !addedAssets.isEmpty ||
210229
!text.isEmpty ||
211230
!addedFileURLs.isEmpty ||
212231
!addedCustomAttachments.isEmpty
@@ -406,7 +425,15 @@ public class MessageComposerViewModel: ObservableObject {
406425

407426
private func checkTypingSuggestions() {
408427
if composerCommand?.displayInfo?.isInstant == true {
409-
// If an instant command is selected, don't check again.
428+
let typingSuggestion = TypingSuggestion(
429+
text: text,
430+
locationRange: NSRange(
431+
location: 0,
432+
length: selectedRangeLocation
433+
)
434+
)
435+
composerCommand?.typingSuggestion = typingSuggestion
436+
showTypingSuggestions()
410437
return
411438
}
412439
composerCommand = commandsHandler.canHandleCommand(

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/CommandsConfig.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ public class DefaultCommandsConfig: CommandsConfig {
3838
commandSymbol: mentionsSymbol,
3939
mentionAllAppUsers: false
4040
)
41-
let giphyCommand = GiphyCommandHandler(
41+
let giphyCommand = GiphyCommandHandler(commandSymbol: "/giphy")
42+
let muteCommand = MuteCommandHandler(
4243
channelController: channelController,
43-
commandSymbol: "/giphy"
44+
commandSymbol: "/mute"
45+
)
46+
let instantCommands = InstantCommandsHandler(
47+
commands: [giphyCommand, muteCommand]
4448
)
45-
let instantCommands = InstantCommandsHandler(commands: [giphyCommand])
4649
return CommandsHandler(commands: [mentionsCommand, instantCommands])
4750
}
4851
}

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/CommandsHandler.swift

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public protocol CommandHandler {
2424
caretLocation: Int
2525
) -> ComposerCommand?
2626

27+
func canShowSuggestions(for command: ComposerCommand) -> CommandHandler?
28+
2729
/// Shows suggestions for the provided command.
2830
/// - Parameter command: the command whose suggestions will be shown.
2931
/// - Returns: `Future` with the suggestions, or an error.
@@ -43,15 +45,37 @@ public protocol CommandHandler {
4345
command: Binding<ComposerCommand?>,
4446
extraData: [String: Any]
4547
)
48+
49+
func canBeExecuted(composerCommand: ComposerCommand) -> Bool
50+
51+
func executeOnMessageSent(
52+
composerCommand: ComposerCommand,
53+
completion: @escaping (Error?) -> Void
54+
)
55+
}
56+
57+
extension CommandHandler {
58+
59+
public func executeOnMessageSent(
60+
composerCommand: ComposerCommand,
61+
completion: @escaping (Error?) -> Void
62+
) {
63+
// optional method.
64+
}
65+
66+
public func canBeExecuted(composerCommand: ComposerCommand) -> Bool {
67+
!composerCommand.typingSuggestion.text.isEmpty
68+
}
4669
}
4770

4871
/// Model for the composer's commands.
4972
public struct ComposerCommand {
5073
/// Identifier of the command.
5174
let id: String
5275
/// Typing suggestion that invokes the command.
53-
let typingSuggestion: TypingSuggestion
76+
var typingSuggestion: TypingSuggestion
5477
let displayInfo: CommandDisplayInfo?
78+
var replacesMessageSent: Bool = false
5579
}
5680

5781
/// Provides information about the suggestion.
@@ -94,13 +118,21 @@ public class CommandsHandler: CommandHandler {
94118
return nil
95119
}
96120

121+
public func canShowSuggestions(for command: ComposerCommand) -> CommandHandler? {
122+
for handler in commands {
123+
if handler.canShowSuggestions(for: command) != nil {
124+
return handler
125+
}
126+
}
127+
128+
return nil
129+
}
130+
97131
public func showSuggestions(
98132
for command: ComposerCommand
99133
) -> Future<SuggestionInfo, Error> {
100-
for handler in commands {
101-
if handler.id == command.id {
102-
return handler.showSuggestions(for: command)
103-
}
134+
if let handler = canShowSuggestions(for: command) {
135+
return handler.showSuggestions(for: command)
104136
}
105137

106138
return StreamChatError.wrongConfig.asFailedPromise()
@@ -112,16 +144,37 @@ public class CommandsHandler: CommandHandler {
112144
command: Binding<ComposerCommand?>,
113145
extraData: [String: Any]
114146
) {
115-
for handler in commands {
116-
let commandValue = command.wrappedValue
117-
if handler.id == commandValue?.id {
118-
handler.handleCommand(
119-
for: text,
120-
selectedRangeLocation: selectedRangeLocation,
121-
command: command,
122-
extraData: extraData
123-
)
124-
}
147+
guard let commandValue = command.wrappedValue else {
148+
return
149+
}
150+
151+
if let handler = canShowSuggestions(for: commandValue), handler.id != id {
152+
handler.handleCommand(
153+
for: text,
154+
selectedRangeLocation: selectedRangeLocation,
155+
command: command,
156+
extraData: extraData
157+
)
125158
}
126159
}
160+
161+
public func executeOnMessageSent(
162+
composerCommand: ComposerCommand,
163+
completion: @escaping (Error?) -> Void
164+
) {
165+
if let handler = canShowSuggestions(for: composerCommand) {
166+
handler.executeOnMessageSent(
167+
composerCommand: composerCommand,
168+
completion: completion
169+
)
170+
}
171+
}
172+
173+
public func canBeExecuted(composerCommand: ComposerCommand) -> Bool {
174+
if let handler = canShowSuggestions(for: composerCommand), handler.id != id {
175+
return handler.canBeExecuted(composerCommand: composerCommand)
176+
}
177+
178+
return !composerCommand.typingSuggestion.text.isEmpty
179+
}
127180
}

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/InstantCommands/GiphyCommandHandler.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,12 @@ public struct GiphyCommandHandler: CommandHandler {
1616
public var displayInfo: CommandDisplayInfo?
1717

1818
private let typingSuggester: TypingSuggester
19-
20-
private let channelController: ChatChannelController
21-
19+
2220
init(
23-
channelController: ChatChannelController,
2421
commandSymbol: String,
2522
id: String = "/giphy"
2623
) {
2724
self.id = id
28-
self.channelController = channelController
2925
typingSuggester = TypingSuggester(
3026
options:
3127
TypingSuggestionOptions(
@@ -66,6 +62,10 @@ public struct GiphyCommandHandler: CommandHandler {
6662
extraData: [String: Any]
6763
) {}
6864

65+
public func canShowSuggestions(for command: ComposerCommand) -> CommandHandler? {
66+
nil
67+
}
68+
6969
public func showSuggestions(
7070
for command: ComposerCommand
7171
) -> Future<SuggestionInfo, Error> {

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/InstantCommands/InstantCommandsHandler.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,20 @@ public class InstantCommandsHandler: CommandHandler {
5454
}
5555
}
5656

57+
public func canShowSuggestions(for command: ComposerCommand) -> CommandHandler? {
58+
for instant in commands {
59+
if instant.canShowSuggestions(for: command) != nil {
60+
return instant
61+
}
62+
}
63+
64+
return command.id == id ? self : nil
65+
}
66+
5767
public func showSuggestions(for command: ComposerCommand) -> Future<SuggestionInfo, Error> {
68+
if let handler = canShowSuggestions(for: command), handler.id != id {
69+
return handler.showSuggestions(for: command)
70+
}
5871
let suggestionInfo = SuggestionInfo(key: id, value: commands)
5972
return resolve(with: suggestionInfo)
6073
}
@@ -65,9 +78,40 @@ public class InstantCommandsHandler: CommandHandler {
6578
command: Binding<ComposerCommand?>,
6679
extraData: [String: Any]
6780
) {
81+
if let commandValue = command.wrappedValue,
82+
let handler = canShowSuggestions(for: commandValue), handler.id != id {
83+
handler.handleCommand(
84+
for: text,
85+
selectedRangeLocation: selectedRangeLocation,
86+
command: command,
87+
extraData: extraData
88+
)
89+
return
90+
}
91+
6892
guard let instantCommand = extraData["instantCommand"] as? ComposerCommand else {
6993
return
7094
}
7195
command.wrappedValue = instantCommand
7296
}
97+
98+
public func executeOnMessageSent(
99+
composerCommand: ComposerCommand,
100+
completion: @escaping (Error?) -> Void
101+
) {
102+
if let handler = canShowSuggestions(for: composerCommand) {
103+
handler.executeOnMessageSent(
104+
composerCommand: composerCommand,
105+
completion: completion
106+
)
107+
}
108+
}
109+
110+
public func canBeExecuted(composerCommand: ComposerCommand) -> Bool {
111+
if let handler = canShowSuggestions(for: composerCommand), handler.id != id {
112+
return handler.canBeExecuted(composerCommand: composerCommand)
113+
}
114+
115+
return !composerCommand.typingSuggestion.text.isEmpty
116+
}
73117
}

0 commit comments

Comments
 (0)