2024-08-07 18:25:29 +02:00

185 lines
5.9 KiB
Swift

/*
* 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 KeychainAccess
import LiveKit
import Logging
import SwiftUI
let sync = ValueStore<Preferences>(store: Keychain(service: "io.openvidu.ios"),
key: "preferences",
default: Preferences())
struct RoomSwitchView: View {
@EnvironmentObject var appCtx: AppContext
@EnvironmentObject var roomCtx: RoomContext
@EnvironmentObject var room: Room
var shouldShowRoomView: Bool {
room.connectionState == .connected || room.connectionState == .reconnecting
}
var shouldShowConfigureUrlsView: Bool {
appCtx.applicationServerUrl.isEmpty || roomCtx.livekitUrl.isEmpty
}
func computeTitle() -> String {
if shouldShowRoomView {
var elements: [String] = []
if let roomName = room.name {
elements.append(roomName)
}
if let localParticipantName = room.localParticipant.name {
elements.append(localParticipantName)
}
if let localParticipantIdentity = room.localParticipant.identity {
elements.append(String(describing: localParticipantIdentity))
}
return elements.joined(separator: " ")
}
return "OpenVidu"
}
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
if shouldShowRoomView {
RoomView()
} else {
if shouldShowConfigureUrlsView {
ConfigureUrlsView()
} else {
ConnectView()
}
}
}
.navigationTitle(computeTitle())
}
}
// Attaches RoomContext and Room to the environment
struct RoomContextView: View {
@EnvironmentObject var appCtx: AppContext
@StateObject var roomCtx = RoomContext(store: sync)
var body: some View {
RoomSwitchView()
.environmentObject(roomCtx)
.environmentObject(roomCtx.room)
.environment(\.colorScheme, .dark)
.foregroundColor(Color.white)
.onDisappear {
print("\(String(describing: type(of: self))) onDisappear")
Task {
await roomCtx.disconnect()
}
}
.onOpenURL(perform: { url in
guard let urlComponent = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
guard let host = url.host else { return }
let secureValue = urlComponent.queryItems?.first(where: { $0.name == "secure" })?.value?.lowercased()
let secure = ["true", "1"].contains { $0 == secureValue }
let tokenValue = urlComponent.queryItems?.first(where: { $0.name == "token" })?.value ?? ""
var builder = URLComponents()
builder.scheme = secure ? "wss" : "ws"
builder.host = host
builder.port = url.port
guard let builtUrl = builder.url?.absoluteString else { return }
print("built URL: \(builtUrl), token: \(tokenValue)")
Task { @MainActor in
roomCtx.livekitUrl = builtUrl
roomCtx.token = tokenValue
if !roomCtx.token.isEmpty {
try await roomCtx.connect()
}
}
})
}
}
extension Decimal {
mutating func round(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) {
var localCopy = self
NSDecimalRound(&self, &localCopy, scale, roundingMode)
}
func rounded(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) -> Decimal {
var result = Decimal()
var localCopy = self
NSDecimalRound(&result, &localCopy, scale, roundingMode)
return result
}
func remainder(of divisor: Decimal) -> Decimal {
let s = self as NSDecimalNumber
let d = divisor as NSDecimalNumber
let b = NSDecimalNumberHandler(roundingMode: .down,
scale: 0,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)
let quotient = s.dividing(by: d, withBehavior: b)
let subtractAmount = quotient.multiplying(by: d)
return s.subtracting(subtractAmount) as Decimal
}
}
@main
struct OpenViduApp: App {
@StateObject var appCtx = AppContext(store: sync)
func nearestSafeScale(for target: Int, scale: Double) -> Decimal {
let p = Decimal(sign: .plus, exponent: -3, significand: 1)
let t = Decimal(target)
var s = Decimal(scale).rounded(3, .down)
while (t * s / 2).remainder(of: 2) != 0 {
s = s + p
}
return s
}
init() {
LoggingSystem.bootstrap {
var logHandler = StreamLogHandler.standardOutput(label: $0)
logHandler.logLevel = .debug
return logHandler
}
}
var body: some Scene {
WindowGroup {
RoomContextView()
.environmentObject(appCtx)
}
.handlesExternalEvents(matching: Set(arrayLiteral: "*"))
}
}