diff --git a/LiveKitLivestreamExample.xcodeproj/project.pbxproj b/LiveKitLivestreamExample.xcodeproj/project.pbxproj index ed2d06b..42a841e 100644 --- a/LiveKitLivestreamExample.xcodeproj/project.pbxproj +++ b/LiveKitLivestreamExample.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 683865A72A29F9A3005D0947 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683865A62A29F9A3005D0947 /* MainView.swift */; }; 683865A92A29F9A4005D0947 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 683865A82A29F9A4005D0947 /* Assets.xcassets */; }; 683865AD2A29F9A4005D0947 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 683865AC2A29F9A4005D0947 /* Preview Assets.xcassets */; }; + 683D7E862B95C0F300AD7EB2 /* ReactionsBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683D7E852B95C0F300AD7EB2 /* ReactionsBarView.swift */; }; 687805892AF9F38000215259 /* LiveKit in Frameworks */ = {isa = PBXBuildFile; productRef = 687805882AF9F38000215259 /* LiveKit */; }; 6878058B2AF9F38000215259 /* LiveKitComponents in Frameworks */ = {isa = PBXBuildFile; productRef = 6878058A2AF9F38000215259 /* LiveKitComponents */; }; 687E311E2A2DCA1600032AE6 /* RoomContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 687E311D2A2DCA1600032AE6 /* RoomContext.swift */; }; @@ -72,6 +73,7 @@ 683865A62A29F9A3005D0947 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; 683865A82A29F9A4005D0947 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 683865AC2A29F9A4005D0947 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 683D7E852B95C0F300AD7EB2 /* ReactionsBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsBarView.swift; sourceTree = ""; }; 687E311D2A2DCA1600032AE6 /* RoomContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContext.swift; sourceTree = ""; }; 687E31222A2E2AE700032AE6 /* StreamView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamView.swift; sourceTree = ""; }; 687E31252A2E396900032AE6 /* SwitchCameraButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCameraButton.swift; sourceTree = ""; }; @@ -211,6 +213,7 @@ 687E31312A2EF8C600032AE6 /* StreamEventsListView.swift */, 68FBA3702A381F650015853E /* StyledButton.swift */, 68FBA3742A3824D80015853E /* StyledTextField.swift */, + 683D7E852B95C0F300AD7EB2 /* ReactionsBarView.swift */, ); path = Views; sourceTree = ""; @@ -382,6 +385,7 @@ 68177E0D2A570F5F00821136 /* ParticipantMetadata.swift in Sources */, 6889F0D32A52AF96004D54FB /* ConnectionDetails.swift in Sources */, 68FBA3752A3824D80015853E /* StyledTextField.swift in Sources */, + 683D7E862B95C0F300AD7EB2 /* ReactionsBarView.swift in Sources */, 687E31322A2EF8C600032AE6 /* StreamEventsListView.swift in Sources */, 68A108C72A5CFC2E00200FAE /* HideKeyboard.swift in Sources */, 687E311E2A2DCA1600032AE6 /* RoomContext.swift in Sources */, diff --git a/Shared/Contexts/RoomContext.swift b/Shared/Contexts/RoomContext.swift index 8acf4e6..765e94e 100644 --- a/Shared/Contexts/RoomContext.swift +++ b/Shared/Contexts/RoomContext.swift @@ -26,7 +26,7 @@ enum DataTopics: String { final class RoomContext: NSObject, ObservableObject { // Private private let api = API(apiBaseURLString: "https://livestream.livekit.io/") - private let room = Room() + let room = Room() enum Step { case welcome @@ -232,19 +232,37 @@ final class RoomContext: NSObject, ObservableObject { } } - public func send() { + public func sendChat() { Task { @MainActor in - let trimmedMessage = self.message.trimmingCharacters(in: .whitespacesAndNewlines) - let chatMessage = ChatMessage(timestamp: 0, - message: trimmedMessage, - participant: self.room.localParticipant) - - events.append(chatMessage) + let eventEntry = try await sendData(string: message) message = "" + events.append(eventEntry) + } + } + + public func sendReaction(string: String) { + Task { @MainActor in + let eventEntry = try await sendData(string: string, isReaction: true) + events.append(eventEntry) + } + } + + public func sendData(string: String, isReaction: Bool = false) async throws -> ChatMessage { + let trimmedString = string.trimmingCharacters(in: .whitespacesAndNewlines) + let chatMessage = ChatMessage(timestamp: 0, + message: trimmedString, + participant: room.localParticipant) - guard let jsonData = try? encoder.encode(chatMessage) else { return } - try await room.localParticipant.publish(data: jsonData, options: DataPublishOptions(topic: DataTopics.chat.rawValue)) + let jsonData = try encoder.encode(chatMessage) + try await room.localParticipant.publish(data: jsonData, options: DataPublishOptions(topic: DataTopics.chat.rawValue)) + + if isReaction { + // Send reaction version + guard let reactionData = trimmedString.data(using: .utf8) else { throw LiveKitError(.invalidState) } + try await room.localParticipant.publish(data: reactionData, options: DataPublishOptions(topic: DataTopics.reaction.rawValue)) } + + return chatMessage } } diff --git a/Shared/StreamView.swift b/Shared/StreamView.swift index 411e28e..fba38ca 100644 --- a/Shared/StreamView.swift +++ b/Shared/StreamView.swift @@ -123,6 +123,8 @@ struct StreamView: View { } } + ReactionsBarView() + MessageBarView(focusFields: _focusedFields, moreAction: { showingOptionsSheet.toggle() diff --git a/Shared/Views/MessageBarView.swift b/Shared/Views/MessageBarView.swift index 6d7adeb..baf0e77 100644 --- a/Shared/Views/MessageBarView.swift +++ b/Shared/Views/MessageBarView.swift @@ -43,7 +43,7 @@ struct MessageBarView: View { isFullWidth: false, isEnabled: roomCtx.canSendMessage) { - roomCtx.send() + roomCtx.sendChat() } label: { Text("Send") } diff --git a/Shared/Views/ReactionsBarView.swift b/Shared/Views/ReactionsBarView.swift new file mode 100644 index 0000000..0a468ef --- /dev/null +++ b/Shared/Views/ReactionsBarView.swift @@ -0,0 +1,55 @@ +/* + * Copyright 2024 LiveKit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import LiveKit +import SwiftUI + +struct ReactionsBarView: View { + @EnvironmentObject var roomCtx: RoomContext + @EnvironmentObject var room: Room + + var body: some View { + HStack(alignment: .center, spacing: 30) { + Button(action: { + roomCtx.sendReaction(string: "🔥") + }, label: { + Text("🔥") + }) + Button(action: { + roomCtx.sendReaction(string: "👏") + }, label: { + Text("👏") + }) + Button(action: { + roomCtx.sendReaction(string: "🤣") + }, label: { + Text("🤣") + }) + Button(action: { + roomCtx.sendReaction(string: "❤️") + }, label: { + Text("❤️") + }) + Button(action: { + roomCtx.sendReaction(string: "🎉") + }, label: { + Text("🎉") + }) + } + .padding(.vertical, 15) + .padding(.horizontal, 10) + } +}