Skip to content

Commit 49e98e9

Browse files
implemented unmute command
1 parent 2bbcf9e commit 49e98e9

File tree

5 files changed

+217
-121
lines changed

5 files changed

+217
-121
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ public class DefaultCommandsConfig: CommandsConfig {
4343
channelController: channelController,
4444
commandSymbol: "/mute"
4545
)
46+
let unmuteCommand = UnmuteCommandHandler(
47+
channelController: channelController,
48+
commandSymbol: "/unmute"
49+
)
4650
let instantCommands = InstantCommandsHandler(
47-
commands: [giphyCommand, muteCommand]
51+
commands: [giphyCommand, muteCommand, unmuteCommand]
4852
)
4953
return CommandsHandler(commands: [mentionsCommand, instantCommands])
5054
}

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

Lines changed: 14 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -6,145 +6,40 @@ import Combine
66
import StreamChat
77
import SwiftUI
88

9-
/// Handles the giphy command and provides suggestions.
10-
public class MuteCommandHandler: CommandHandler {
11-
12-
@Injected(\.chatClient) private var chatClient
13-
@Injected(\.images) private var images
14-
@Injected(\.colors) private var colors
15-
16-
private let channelController: ChatChannelController
17-
private let mentionsCommandHandler: MentionsCommandHandler
18-
19-
private var mutedUser: ChatUser?
9+
/// Handles the mute command.
10+
public class MuteCommandHandler: TwoStepMentionCommand {
2011

21-
public let id: String
22-
public var displayInfo: CommandDisplayInfo?
23-
public let replacesMessageSending: Bool = true
24-
12+
@Injected(\.images) private var images
13+
@Injected(\.chatClient) private var chatClient
14+
2515
init(
2616
channelController: ChatChannelController,
2717
commandSymbol: String,
2818
id: String = "/mute"
2919
) {
30-
self.channelController = channelController
31-
self.id = id
32-
mentionsCommandHandler = MentionsCommandHandler(
20+
super.init(
3321
channelController: channelController,
34-
commandSymbol: "@",
35-
mentionAllAppUsers: false
22+
commandSymbol: commandSymbol,
23+
id: id
3624
)
37-
displayInfo = CommandDisplayInfo(
25+
let displayInfo = CommandDisplayInfo(
3826
displayName: "Mute",
3927
icon: images.commandMute,
40-
format: "\(id) [text]",
28+
format: "\(id) [@username]",
4129
isInstant: true
4230
)
31+
self.displayInfo = displayInfo
4332
}
44-
45-
public func canHandleCommand(in text: String, caretLocation: Int) -> ComposerCommand? {
46-
if text == id {
47-
return ComposerCommand(
48-
id: id,
49-
typingSuggestion: TypingSuggestion(
50-
text: text,
51-
locationRange: NSRange(
52-
location: 0,
53-
length: caretLocation
54-
)
55-
),
56-
displayInfo: displayInfo,
57-
replacesMessageSent: true
58-
)
59-
} else {
60-
return nil
61-
}
62-
}
63-
64-
public func handleCommand(
65-
for text: Binding<String>,
66-
selectedRangeLocation: Binding<Int>,
67-
command: Binding<ComposerCommand?>,
68-
extraData: [String: Any]
69-
) {
70-
guard let chatUser = extraData["chatUser"] as? ChatUser,
71-
let typingSuggestionValue = command.wrappedValue?.typingSuggestion else {
72-
return
73-
}
74-
75-
mutedUser = chatUser
76-
77-
let mentionText = self.mentionText(for: chatUser)
78-
let newText = (text.wrappedValue as NSString).replacingCharacters(
79-
in: typingSuggestionValue.locationRange,
80-
with: mentionText
81-
)
82-
text.wrappedValue = newText
8333

84-
let newCaretLocation =
85-
selectedRangeLocation.wrappedValue + (mentionText.count - typingSuggestionValue.text.count)
86-
selectedRangeLocation.wrappedValue = newCaretLocation
87-
}
88-
89-
public func canBeExecuted(composerCommand: ComposerCommand) -> Bool {
90-
mutedUser != nil
91-
}
92-
93-
private func mentionText(for user: ChatUser) -> String {
94-
if let name = user.name, !name.isEmpty {
95-
return "@\(name)"
96-
} else {
97-
return "@\(user.id)"
98-
}
99-
}
100-
101-
public func commandHandler(for command: ComposerCommand) -> CommandHandler? {
102-
if let mutedUser = mutedUser,
103-
command.typingSuggestion.text != mentionText(for: mutedUser) {
104-
self.mutedUser = nil
105-
}
106-
return command.id == id ? self : nil
107-
}
108-
109-
public func showSuggestions(
110-
for command: ComposerCommand
111-
) -> Future<SuggestionInfo, Error> {
112-
if mutedUser != nil {
113-
return resolve(
114-
with: SuggestionInfo(
115-
key: "mentions",
116-
value: []
117-
)
118-
)
119-
}
120-
let oldText = command.typingSuggestion.text
121-
let text = oldText.replacingOccurrences(
122-
of: "@", with: ""
123-
).trimmingCharacters(in: .whitespaces)
124-
let oldRange = command.typingSuggestion.locationRange
125-
let offset = oldText.count - text.count
126-
let newRange = NSRange(
127-
location: 0,
128-
length: oldRange.location - offset
129-
)
130-
let typingSuggestion = TypingSuggestion(text: text, locationRange: newRange)
131-
let updated = ComposerCommand(
132-
id: command.id,
133-
typingSuggestion: typingSuggestion,
134-
displayInfo: command.displayInfo
135-
)
136-
return mentionsCommandHandler.showSuggestions(for: updated)
137-
}
138-
139-
public func executeOnMessageSent(
34+
override public func executeOnMessageSent(
14035
composerCommand: ComposerCommand,
14136
completion: @escaping (Error?) -> Void
14237
) {
143-
if let mutedUser = mutedUser {
38+
if let mutedUser = selectedUser {
14439
chatClient
14540
.userController(userId: mutedUser.id)
14641
.mute { [weak self] error in
147-
self?.mutedUser = nil
42+
self?.selectedUser = nil
14843
completion(error)
14944
}
15045

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Combine
6+
import StreamChat
7+
import SwiftUI
8+
9+
/// Base class that supports two step commands, where the second one is mentioning users.
10+
public class TwoStepMentionCommand: CommandHandler {
11+
12+
@Injected(\.images) private var images
13+
@Injected(\.colors) private var colors
14+
15+
private let channelController: ChatChannelController
16+
private let mentionsCommandHandler: MentionsCommandHandler
17+
18+
internal var selectedUser: ChatUser?
19+
20+
public let id: String
21+
public var displayInfo: CommandDisplayInfo?
22+
public let replacesMessageSending: Bool = true
23+
24+
init(
25+
channelController: ChatChannelController,
26+
commandSymbol: String,
27+
id: String,
28+
displayInfo: CommandDisplayInfo? = nil
29+
) {
30+
self.channelController = channelController
31+
self.id = id
32+
mentionsCommandHandler = MentionsCommandHandler(
33+
channelController: channelController,
34+
commandSymbol: "@",
35+
mentionAllAppUsers: false
36+
)
37+
self.displayInfo = displayInfo
38+
}
39+
40+
public func canHandleCommand(in text: String, caretLocation: Int) -> ComposerCommand? {
41+
if text == id {
42+
return ComposerCommand(
43+
id: id,
44+
typingSuggestion: TypingSuggestion(
45+
text: text,
46+
locationRange: NSRange(
47+
location: 0,
48+
length: caretLocation
49+
)
50+
),
51+
displayInfo: displayInfo,
52+
replacesMessageSent: true
53+
)
54+
} else {
55+
return nil
56+
}
57+
}
58+
59+
public func handleCommand(
60+
for text: Binding<String>,
61+
selectedRangeLocation: Binding<Int>,
62+
command: Binding<ComposerCommand?>,
63+
extraData: [String: Any]
64+
) {
65+
guard let chatUser = extraData["chatUser"] as? ChatUser,
66+
let typingSuggestionValue = command.wrappedValue?.typingSuggestion else {
67+
return
68+
}
69+
70+
selectedUser = chatUser
71+
72+
let mentionText = self.mentionText(for: chatUser)
73+
let newText = (text.wrappedValue as NSString).replacingCharacters(
74+
in: typingSuggestionValue.locationRange,
75+
with: mentionText
76+
)
77+
text.wrappedValue = newText
78+
79+
let newCaretLocation =
80+
selectedRangeLocation.wrappedValue + (mentionText.count - typingSuggestionValue.text.count)
81+
selectedRangeLocation.wrappedValue = newCaretLocation
82+
}
83+
84+
public func canBeExecuted(composerCommand: ComposerCommand) -> Bool {
85+
selectedUser != nil
86+
}
87+
88+
private func mentionText(for user: ChatUser) -> String {
89+
if let name = user.name, !name.isEmpty {
90+
return "@\(name)"
91+
} else {
92+
return "@\(user.id)"
93+
}
94+
}
95+
96+
public func commandHandler(for command: ComposerCommand) -> CommandHandler? {
97+
if let selectedUser = selectedUser,
98+
command.typingSuggestion.text != mentionText(for: selectedUser) {
99+
self.selectedUser = nil
100+
}
101+
return command.id == id ? self : nil
102+
}
103+
104+
public func showSuggestions(
105+
for command: ComposerCommand
106+
) -> Future<SuggestionInfo, Error> {
107+
if selectedUser != nil {
108+
return resolve(
109+
with: SuggestionInfo(
110+
key: "mentions",
111+
value: []
112+
)
113+
)
114+
}
115+
let oldText = command.typingSuggestion.text
116+
let text = oldText.replacingOccurrences(
117+
of: "@", with: ""
118+
).trimmingCharacters(in: .whitespaces)
119+
let oldRange = command.typingSuggestion.locationRange
120+
let offset = oldText.count - text.count
121+
let newRange = NSRange(
122+
location: 0,
123+
length: oldRange.location - offset
124+
)
125+
let typingSuggestion = TypingSuggestion(text: text, locationRange: newRange)
126+
let updated = ComposerCommand(
127+
id: command.id,
128+
typingSuggestion: typingSuggestion,
129+
displayInfo: command.displayInfo
130+
)
131+
return mentionsCommandHandler.showSuggestions(for: updated)
132+
}
133+
134+
public func executeOnMessageSent(
135+
composerCommand: ComposerCommand,
136+
completion: @escaping (Error?) -> Void
137+
) {
138+
// Implement in subclasses.
139+
}
140+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Combine
6+
import StreamChat
7+
import SwiftUI
8+
9+
/// Handles the unmute command.
10+
public class UnmuteCommandHandler: TwoStepMentionCommand {
11+
12+
@Injected(\.images) private var images
13+
@Injected(\.chatClient) private var chatClient
14+
15+
init(
16+
channelController: ChatChannelController,
17+
commandSymbol: String,
18+
id: String = "/unmute"
19+
) {
20+
super.init(
21+
channelController: channelController,
22+
commandSymbol: commandSymbol,
23+
id: id
24+
)
25+
let displayInfo = CommandDisplayInfo(
26+
displayName: "Unmute",
27+
icon: images.commandUnmute,
28+
format: "\(id) [@username]",
29+
isInstant: true
30+
)
31+
self.displayInfo = displayInfo
32+
}
33+
34+
override public func executeOnMessageSent(
35+
composerCommand: ComposerCommand,
36+
completion: @escaping (Error?) -> Void
37+
) {
38+
if let mutedUser = selectedUser {
39+
chatClient
40+
.userController(userId: mutedUser.id)
41+
.unmute { [weak self] error in
42+
self?.selectedUser = nil
43+
completion(error)
44+
}
45+
46+
return
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)