mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-11 09:10:20 +00:00
Compare commits
10 Commits
6806
...
ios_locksc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1893ced556 | ||
|
|
7d9c13a618 | ||
|
|
48ed3b7dc6 | ||
|
|
04abfe1a3b | ||
|
|
d45decc393 | ||
|
|
88f8f48465 | ||
|
|
8127ea2479 | ||
|
|
b22915c169 | ||
|
|
14fcd153e5 | ||
|
|
48efe36cdf |
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
ios/app/Widgets Extension/Assets.xcassets/Contents.json
Normal file
6
ios/app/Widgets Extension/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
12
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "end_call_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/end_call_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/leave_meeting.imageset/end_call_button.pdf
vendored
Normal file
Binary file not shown.
12
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "microphone_off_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/microphone_off_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_off.imageset/microphone_off_button.pdf
vendored
Normal file
Binary file not shown.
12
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/Contents.json
vendored
Normal file
12
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "microphone_on_button.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/microphone_on_button.pdf
vendored
Normal file
BIN
ios/app/Widgets Extension/Assets.xcassets/microphone_on.imageset/microphone_on_button.pdf
vendored
Normal file
Binary file not shown.
101
ios/app/Widgets Extension/DarwinNotificationsObserver.swift
Normal file
101
ios/app/Widgets Extension/DarwinNotificationsObserver.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// DarwinNotificationsObserver.swift
|
||||
// WidgetsExtension
|
||||
//
|
||||
// Created by Alex Bumbu on 17.10.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum DarwinNotification: String {
|
||||
case meetingMutedChanged = "iOS_MeetingMutedChanged"
|
||||
}
|
||||
|
||||
extension DarwinNotification {
|
||||
|
||||
var name: String { rawValue }
|
||||
}
|
||||
|
||||
class DarwinNotificationsObserver {
|
||||
|
||||
private static var observers = Array<ProxyObserver>()
|
||||
|
||||
private let queue = DispatchQueue(label: "org.jitsi.meet.darwinNotificationObserver", qos: .default, autoreleaseFrequency: .workItem)
|
||||
private var notificationCenter: CFNotificationCenter
|
||||
|
||||
init() {
|
||||
notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
}
|
||||
|
||||
func observe(notification: DarwinNotification, handler: @escaping () -> Void) {
|
||||
let proxyObserver = ProxyObserver(observer: self, notificationName: notification.name, handler: handler)
|
||||
queue.async {
|
||||
DarwinNotificationsObserver.observers.append(proxyObserver)
|
||||
}
|
||||
|
||||
let callback: CFNotificationCallback = { _, observer, name, _, _ in
|
||||
guard let observer = observer else {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract pointer to `observer` from void pointer:
|
||||
let proxyObserver = Unmanaged<ProxyObserver>.fromOpaque(observer).takeUnretainedValue()
|
||||
var observers = DarwinNotificationsObserver.observers
|
||||
if !proxyObserver.forwardNotification(), let index = observers.firstIndex(of: proxyObserver) {
|
||||
// cleanup if `forwardNotification` fails
|
||||
observers.remove(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
CFNotificationCenterAddObserver(notificationCenter,
|
||||
Unmanaged.passUnretained(proxyObserver).toOpaque(),
|
||||
callback,
|
||||
notification.name as CFString,
|
||||
nil,
|
||||
.deliverImmediately)
|
||||
}
|
||||
|
||||
func stopObserving(notification: DarwinNotification) {
|
||||
queue.sync {
|
||||
DarwinNotificationsObserver.observers.removeAll { $0.observer == nil }
|
||||
}
|
||||
|
||||
if let index = DarwinNotificationsObserver.observers.firstIndex(where: { $0.observer === self && $0.notificationName == notification.name }) {
|
||||
let proxyObserver = DarwinNotificationsObserver.observers[index]
|
||||
CFNotificationCenterRemoveObserver(notificationCenter,
|
||||
Unmanaged.passUnretained(proxyObserver).toOpaque(),
|
||||
CFNotificationName(notification.name as CFString),
|
||||
nil)
|
||||
queue.async {
|
||||
DarwinNotificationsObserver.observers.remove(at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyObserver: Equatable {
|
||||
|
||||
let notificationName: String
|
||||
weak var observer: AnyObject?
|
||||
|
||||
private let handler: () -> (Void)
|
||||
|
||||
static func == (lhs: ProxyObserver, rhs: ProxyObserver) -> Bool {
|
||||
lhs.observer === rhs.observer && lhs.notificationName == rhs.notificationName
|
||||
}
|
||||
|
||||
init(observer: AnyObject? = nil, notificationName: String, handler: @escaping () -> Void) {
|
||||
self.notificationName = notificationName
|
||||
self.handler = handler
|
||||
self.observer = observer
|
||||
}
|
||||
|
||||
func forwardNotification() -> Bool {
|
||||
guard observer != nil else {
|
||||
return false
|
||||
}
|
||||
|
||||
handler()
|
||||
return true
|
||||
}
|
||||
}
|
||||
11
ios/app/Widgets Extension/Info.plist
Normal file
11
ios/app/Widgets Extension/Info.plist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
54
ios/app/Widgets Extension/LockScreenLeaveMeetingWidget.swift
Normal file
54
ios/app/Widgets Extension/LockScreenLeaveMeetingWidget.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// LockScreenLeaveMeetingWidget.swift
|
||||
// WidgetsExtension
|
||||
//
|
||||
// Created by Alex Bumbu on 31.10.2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
struct LockScreenLeaveMeetingWidget: Widget {
|
||||
let kind: String = "LockScreenLeaveMeetingWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
WidgetsEntryView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Leave Jitsi Meeting Widget")
|
||||
.description("This is a lockscreen widget for leaving the ongoing Jitsi meeting.")
|
||||
.supportedFamilies([.accessoryCircular])
|
||||
}
|
||||
}
|
||||
|
||||
//struct LockScreenLeaveMeetingWidget_Preview: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// let meetingState = MeetingState(audioMuted: true)
|
||||
//
|
||||
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
|
||||
// .previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||
// .previewDisplayName("Circular")
|
||||
// }
|
||||
//}
|
||||
|
||||
private struct WidgetsEntryView: View {
|
||||
@Environment(\.widgetFamily) var widgetFamily
|
||||
var entry: Provider.Entry
|
||||
|
||||
var body: some View {
|
||||
if entry.meetingState != nil, widgetFamily == .accessoryCircular {
|
||||
AccessoryCircularWidgetView()
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AccessoryCircularWidgetView: View {
|
||||
var body: some View {
|
||||
Image("leave_meeting")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.widgetURL(URL(string: "meet/leaveMeeting")!)
|
||||
}
|
||||
}
|
||||
89
ios/app/Widgets Extension/LockScreenMuteAudioWidget.swift
Normal file
89
ios/app/Widgets Extension/LockScreenMuteAudioWidget.swift
Normal file
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// LockScreenMuteAudioWidget.swift
|
||||
// WidgetsExtension
|
||||
//
|
||||
// Created by Alex Bumbu on 31.10.2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
struct LockScreenMuteAudioWidget: Widget {
|
||||
let kind: String = "LockScreenMuteAudioWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
WidgetsEntryView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Mute Jitsi Audio Widget")
|
||||
.description("This is a lockscreen widget for muting or unmuting the audio for the ongoing Jitsi meeting.")
|
||||
.supportedFamilies([.accessoryCircular])
|
||||
}
|
||||
}
|
||||
|
||||
//struct LockScreenMuteAudioWidget_Preview: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// let meetingState = MeetingState(audioMuted: true)
|
||||
//
|
||||
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
|
||||
// .previewContext(WidgetPreviewContext(family: .accessoryInline))
|
||||
// .previewDisplayName("Inline")
|
||||
//
|
||||
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
|
||||
// .previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||
// .previewDisplayName("Circular")
|
||||
//
|
||||
// WidgetsEntryView(entry: CurrentMeetingEntry(date: Date(), meetingState: meetingState))
|
||||
// .previewContext(WidgetPreviewContext(family: .accessoryRectangular))
|
||||
// .previewDisplayName("Rectangular")
|
||||
// }
|
||||
//}
|
||||
|
||||
private struct WidgetsEntryView: View {
|
||||
@Environment(\.widgetFamily) var widgetFamily
|
||||
var entry: Provider.Entry
|
||||
|
||||
var body: some View {
|
||||
if let meetingState = entry.meetingState {
|
||||
switch widgetFamily {
|
||||
case .accessoryInline:
|
||||
Text("Some meeting name")
|
||||
case .accessoryRectangular:
|
||||
AccessoryCircularWidgetView(audioMuted: meetingState.audioMuted)
|
||||
case .accessoryCircular:
|
||||
AccessoryCircularWidgetView(audioMuted: meetingState.audioMuted)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AccessoryRectangularWidgetView: View {
|
||||
var audioMuted: Bool
|
||||
|
||||
var body: some View {
|
||||
let imageName: String = audioMuted ? "microphone_on" : "microphone_off"
|
||||
let caption: String = audioMuted ? "Unmute \naudio" : "Mute \naudio"
|
||||
HStack {
|
||||
Image(imageName)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
Text(caption)
|
||||
}.widgetURL(URL(string: "meet/toggleAudioMute")!)
|
||||
}
|
||||
}
|
||||
|
||||
private struct AccessoryCircularWidgetView: View {
|
||||
var audioMuted: Bool
|
||||
|
||||
var body: some View {
|
||||
let imageName: String = audioMuted ? "microphone_on" : "microphone_off"
|
||||
Image(imageName)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.widgetURL(URL(string: "meet/toggleAudioMute")!)
|
||||
}
|
||||
}
|
||||
30
ios/app/Widgets Extension/MeetingState.swift
Normal file
30
ios/app/Widgets Extension/MeetingState.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MeetingState.swift
|
||||
// WidgetsExtension
|
||||
//
|
||||
// Created by Alex Bumbu on 28.10.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct MeetingState: Decodable {
|
||||
var audioMuted: Bool
|
||||
}
|
||||
|
||||
extension MeetingState {
|
||||
private static var stateFileURL: URL? {
|
||||
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.org.jitsi.meet.appgroup")?.appending(component: "widgetState")
|
||||
}
|
||||
|
||||
static func load() -> MeetingState? {
|
||||
guard
|
||||
let stateFileURL = stateFileURL,
|
||||
let data = try? Data(contentsOf: stateFileURL)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let decoder = PropertyListDecoder()
|
||||
return try? decoder.decode(MeetingState.self, from: data)
|
||||
}
|
||||
}
|
||||
44
ios/app/Widgets Extension/Provider.swift
Normal file
44
ios/app/Widgets Extension/Provider.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Provider.swift
|
||||
// WidgetsExtension
|
||||
//
|
||||
// Created by Alex Bumbu on 31.10.2022.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct CurrentMeetingEntry: TimelineEntry {
|
||||
let date: Date
|
||||
var meetingState: MeetingState?
|
||||
}
|
||||
|
||||
class Provider: TimelineProvider {
|
||||
private var currentMeetingState: MeetingState? {
|
||||
return MeetingState.load()
|
||||
}
|
||||
|
||||
func placeholder(in context: Context) -> CurrentMeetingEntry {
|
||||
CurrentMeetingEntry(date: Date(),
|
||||
meetingState: MeetingState(audioMuted: false))
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (CurrentMeetingEntry) -> ()) {
|
||||
var meetingState = currentMeetingState
|
||||
if context.isPreview {
|
||||
meetingState = MeetingState(audioMuted: false)
|
||||
}
|
||||
|
||||
let entry = CurrentMeetingEntry(date: Date(), meetingState: meetingState)
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<CurrentMeetingEntry>) -> ()) {
|
||||
var entries: [CurrentMeetingEntry] = []
|
||||
let entry = CurrentMeetingEntry(date: Date(), meetingState: currentMeetingState)
|
||||
entries.append(entry)
|
||||
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
completion(timeline)
|
||||
}
|
||||
}
|
||||
19
ios/app/Widgets Extension/Widgets.swift
Normal file
19
ios/app/Widgets Extension/Widgets.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Widgets.swift
|
||||
// Widgets
|
||||
//
|
||||
// Created by Alex Bumbu on 17.10.2022.
|
||||
// Copyright © 2022 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct Widgets: WidgetBundle {
|
||||
@WidgetBundleBuilder
|
||||
var body: some Widget {
|
||||
LockScreenMuteAudioWidget()
|
||||
LockScreenLeaveMeetingWidget()
|
||||
}
|
||||
}
|
||||
10
ios/app/WidgetsExtension.entitlements
Normal file
10
ios/app/WidgetsExtension.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.jitsi.meet.appgroup</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -23,14 +23,26 @@
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */; };
|
||||
4E46D952290FF39E00761DEF /* LockScreenMuteAudioWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */; };
|
||||
4E46D954290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */; };
|
||||
4E6920B828FD84D700645D9E /* DarwinNotificationsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */; };
|
||||
4E6A3E17291024B900E6B0B5 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6A3E16291024B900E6B0B5 /* Provider.swift */; };
|
||||
4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E90F93F2632D1AB001102D4 /* Atomic.swift */; };
|
||||
4EA73DA3290C1D6C00A16FF8 /* MeetingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */; };
|
||||
4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
|
||||
4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06026260E026600F524C5 /* SampleHandler.swift */; };
|
||||
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06039260E09D000F524C5 /* SocketConnection.swift */; };
|
||||
4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */; };
|
||||
4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603B260E09D000F524C5 /* SampleUploader.swift */; };
|
||||
4EBB458A28FFFD4100855769 /* RoutesHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBB458928FFFD4100855769 /* RoutesHandler.m */; };
|
||||
4EBB458E2902E85B00855769 /* WidgetKitHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */; };
|
||||
4ECA496628FD590000085365 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ECA496528FD590000085365 /* WidgetKit.framework */; };
|
||||
4ECA496828FD590000085365 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ECA496728FD590000085365 /* SwiftUI.framework */; };
|
||||
4ECA496B28FD590000085365 /* Widgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECA496A28FD590000085365 /* Widgets.swift */; };
|
||||
4ECA496D28FD590000085365 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4ECA496C28FD590000085365 /* Assets.xcassets */; };
|
||||
4ECA497128FD590000085365 /* WidgetsExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4ECA496428FD590000085365 /* WidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
C2116A7673E01A1CCD5DC1F4 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */; };
|
||||
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */; };
|
||||
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; };
|
||||
DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -64,6 +76,13 @@
|
||||
remoteGlobalIDString = 4EB06022260E026600F524C5;
|
||||
remoteInfo = "JitsiMeetBroadcast Extension";
|
||||
};
|
||||
4ECA496F28FD590000085365 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 4ECA496328FD590000085365;
|
||||
remoteInfo = WidgetsExtension;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -108,6 +127,7 @@
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
4ECA497128FD590000085365 /* WidgetsExtension.appex in Embed App Extensions */,
|
||||
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
@@ -134,6 +154,7 @@
|
||||
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = "<group>"; };
|
||||
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
@@ -141,8 +162,12 @@
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenMuteAudioWidget.swift; sourceTree = "<group>"; };
|
||||
4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenLeaveMeetingWidget.swift; sourceTree = "<group>"; };
|
||||
4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarwinNotificationsObserver.swift; sourceTree = "<group>"; };
|
||||
4E6A3E16291024B900E6B0B5 /* Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = "<group>"; };
|
||||
4E90F93F2632D1AB001102D4 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
||||
4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingState.swift; sourceTree = "<group>"; };
|
||||
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = JitsiMeetBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4EB06026260E026600F524C5 /* SampleHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = "<group>"; };
|
||||
4EB06028260E026600F524C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
@@ -150,10 +175,21 @@
|
||||
4EB06039260E09D000F524C5 /* SocketConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketConnection.swift; sourceTree = "<group>"; };
|
||||
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
|
||||
4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
|
||||
4EBB458828FFFD4100855769 /* RoutesHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoutesHandler.h; sourceTree = "<group>"; };
|
||||
4EBB458928FFFD4100855769 /* RoutesHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoutesHandler.m; sourceTree = "<group>"; };
|
||||
4EBB458B2902A94700855769 /* WidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetsExtension.entitlements; sourceTree = "<group>"; };
|
||||
4EBB458C2902E85B00855769 /* JitsiMeet-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeet-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetKitHelper.swift; sourceTree = "<group>"; };
|
||||
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
|
||||
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
4ECA496428FD590000085365 /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4ECA496528FD590000085365 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
|
||||
4ECA496728FD590000085365 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
4ECA496A28FD590000085365 /* Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widgets.swift; sourceTree = "<group>"; };
|
||||
4ECA496C28FD590000085365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
4ECA496E28FD590000085365 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
|
||||
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DE050388256E904600DEE3A5 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/apple/WebRTC.xcframework"; sourceTree = "<group>"; };
|
||||
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUtilities.m; sourceTree = "<group>"; };
|
||||
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRUtilities.h; sourceTree = "<group>"; };
|
||||
@@ -180,7 +216,7 @@
|
||||
DED016F128ECBC9D009D5E8D /* WebRTC.xcframework in Frameworks */,
|
||||
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */,
|
||||
FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */,
|
||||
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */,
|
||||
C2116A7673E01A1CCD5DC1F4 /* libPods-JitsiMeet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -199,6 +235,15 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4ECA496128FD590000085365 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4ECA496828FD590000085365 /* SwiftUI.framework in Frameworks */,
|
||||
4ECA496628FD590000085365 /* WidgetKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -213,7 +258,9 @@
|
||||
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
|
||||
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
|
||||
4EC49B8625BED71300E76218 /* ReplayKit.framework */,
|
||||
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */,
|
||||
4ECA496528FD590000085365 /* WidgetKit.framework */,
|
||||
4ECA496728FD590000085365 /* SwiftUI.framework */,
|
||||
0BF9FEBA4DEAB800AD735681 /* libPods-JitsiMeet.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -261,6 +308,10 @@
|
||||
0BBD021F212EB69D00CCB19F /* Types.h */,
|
||||
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */,
|
||||
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */,
|
||||
4EBB458828FFFD4100855769 /* RoutesHandler.h */,
|
||||
4EBB458928FFFD4100855769 /* RoutesHandler.m */,
|
||||
4EBB458D2902E85B00855769 /* WidgetKitHelper.swift */,
|
||||
4EBB458C2902E85B00855769 /* JitsiMeet-Bridging-Header.h */,
|
||||
);
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
@@ -282,9 +333,25 @@
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 4;
|
||||
};
|
||||
4ECA496928FD590000085365 /* Widgets Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4ECA496A28FD590000085365 /* Widgets.swift */,
|
||||
4E46D951290FF39E00761DEF /* LockScreenMuteAudioWidget.swift */,
|
||||
4E46D953290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift */,
|
||||
4E6A3E16291024B900E6B0B5 /* Provider.swift */,
|
||||
4EA73DA2290C1D6C00A16FF8 /* MeetingState.swift */,
|
||||
4E6920B728FD84D700645D9E /* DarwinNotificationsObserver.swift */,
|
||||
4ECA496C28FD590000085365 /* Assets.xcassets */,
|
||||
4ECA496E28FD590000085365 /* Info.plist */,
|
||||
);
|
||||
path = "Widgets Extension";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EBB458B2902A94700855769 /* WidgetsExtension.entitlements */,
|
||||
B3B083EB1D4955FF0069CEE7 /* app.entitlements */,
|
||||
0B26BE711EC5BC4D00EEFB41 /* Frameworks */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
@@ -292,11 +359,12 @@
|
||||
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
|
||||
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
|
||||
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
|
||||
CDD71F5E1157E9F283DF92A8 /* Pods */,
|
||||
4ECA496928FD590000085365 /* Widgets Extension */,
|
||||
BD4E28FA984EA7018FD927DF /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
tabWidth = 4;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
@@ -305,15 +373,16 @@
|
||||
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
|
||||
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
|
||||
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
|
||||
4ECA496428FD590000085365 /* WidgetsExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CDD71F5E1157E9F283DF92A8 /* Pods */ = {
|
||||
BD4E28FA984EA7018FD927DF /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */,
|
||||
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */,
|
||||
7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */,
|
||||
8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = ../Pods;
|
||||
@@ -361,7 +430,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
|
||||
buildPhases = (
|
||||
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */,
|
||||
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */,
|
||||
0BBA83C41EC9F7600075A103 /* Run React packager */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
@@ -380,6 +449,7 @@
|
||||
dependencies = (
|
||||
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
|
||||
4EB0602A260E026600F524C5 /* PBXTargetDependency */,
|
||||
4ECA497028FD590000085365 /* PBXTargetDependency */,
|
||||
);
|
||||
name = JitsiMeet;
|
||||
productName = "Jitsi Meet";
|
||||
@@ -403,15 +473,32 @@
|
||||
productReference = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
4ECA496328FD590000085365 /* WidgetsExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 4ECA497428FD590100085365 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */;
|
||||
buildPhases = (
|
||||
4ECA496028FD590000085365 /* Sources */,
|
||||
4ECA496128FD590000085365 /* Frameworks */,
|
||||
4ECA496228FD590000085365 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = WidgetsExtension;
|
||||
productName = WidgetsExtension;
|
||||
productReference = 4ECA496428FD590000085365 /* WidgetsExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1240;
|
||||
LastSwiftUpdateCheck = 1400;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
0BEA5C241F7B8F73000D0AB4 = {
|
||||
CreatedOnToolsVersion = 9.0;
|
||||
@@ -424,6 +511,7 @@
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1400;
|
||||
SystemCapabilities = {
|
||||
com.apple.SafariKeychain = {
|
||||
enabled = 1;
|
||||
@@ -436,6 +524,9 @@
|
||||
4EB06022260E026600F524C5 = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
};
|
||||
4ECA496328FD590000085365 = {
|
||||
CreatedOnToolsVersion = 14.0.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
|
||||
@@ -455,6 +546,7 @@
|
||||
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
|
||||
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
|
||||
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
|
||||
4ECA496328FD590000085365 /* WidgetsExtension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -494,6 +586,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4ECA496228FD590000085365 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4ECA496D28FD590000085365 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -543,7 +643,7 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if test \"$PRODUCT_BUNDLE_IDENTIFIER\" = \"com.atlassian.JitsiMeet.ios\"; then\n ENTITLEMENTS_PLIST=\"$PROJECT_DIR/app.entitlements\"\n \n /usr/libexec/PlistBuddy -c \"Add :com.apple.developer.avfoundation.multitasking-camera-access bool 1\" $ENTITLEMENTS_PLIST\nfi\n";
|
||||
};
|
||||
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */ = {
|
||||
58E2CB346F2C2A873294F481 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -642,8 +742,10 @@
|
||||
files = (
|
||||
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */,
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
||||
4EBB458E2902E85B00855769 /* WidgetKitHelper.swift in Sources */,
|
||||
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
4EBB458A28FFFD4100855769 /* RoutesHandler.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -659,6 +761,19 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4ECA496028FD590000085365 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4ECA496B28FD590000085365 /* Widgets.swift in Sources */,
|
||||
4E6920B828FD84D700645D9E /* DarwinNotificationsObserver.swift in Sources */,
|
||||
4E46D954290FF55600761DEF /* LockScreenLeaveMeetingWidget.swift in Sources */,
|
||||
4EA73DA3290C1D6C00A16FF8 /* MeetingState.swift in Sources */,
|
||||
4E6A3E17291024B900E6B0B5 /* Provider.swift in Sources */,
|
||||
4E46D952290FF39E00761DEF /* LockScreenMuteAudioWidget.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -677,6 +792,11 @@
|
||||
target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
|
||||
targetProxy = 4EB06029260E026600F524C5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
4ECA497028FD590000085365 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 4ECA496328FD590000085365 /* WidgetsExtension */;
|
||||
targetProxy = 4ECA496F28FD590000085365 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -845,11 +965,12 @@
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */;
|
||||
baseConfigurationReference = 7052390E12D7319D36D8E4CA /* Pods-JitsiMeet.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -871,16 +992,20 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet;
|
||||
PRODUCT_NAME = "jitsi-meet";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "src/JitsiMeet-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */;
|
||||
baseConfigurationReference = 8CAA3C5A38E868335D1C1EC1 /* Pods-JitsiMeet.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -901,6 +1026,8 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet;
|
||||
PRODUCT_NAME = "jitsi-meet";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "src/JitsiMeet-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -973,6 +1100,91 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
4ECA497228FD590100085365 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Widgets Extension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.widgets.extension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4ECA497328FD590100085365 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Widgets Extension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.widgets.extension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -1126,6 +1338,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
4ECA497428FD590100085365 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4ECA497228FD590100085365 /* Debug */,
|
||||
4ECA497328FD590100085365 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1400"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4ECA496328FD590000085365"
|
||||
BuildableName = "WidgetsExtension.appex"
|
||||
BlueprintName = "WidgetsExtension"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "JitsiMeet"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.springboard">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4ECA496328FD590000085365"
|
||||
BuildableName = "WidgetsExtension.appex"
|
||||
BlueprintName = "WidgetsExtension"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "JitsiMeet"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = "LockScreenLeaveMeetingWidget"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "medium"
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "JitsiMeet"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "FIRUtilities.h"
|
||||
#import "RoutesHandler.h"
|
||||
#import "Types.h"
|
||||
#import "ViewController.h"
|
||||
|
||||
@@ -69,7 +70,7 @@
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
|
||||
|
||||
|
||||
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
|
||||
// 1. Attempt to handle Universal Links through Firebase in order to support
|
||||
// its Dynamic Links (which we utilize for the purposes of deferred deep
|
||||
@@ -107,6 +108,10 @@
|
||||
if ([[url absoluteString] containsString:@"google/link/?dismiss=1&is_weak_match=1"]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([[RoutesHandler sharedInstance] routeURL:url]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSURL *openUrl = url;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
ios/app/src/JitsiMeet-Bridging-Header.h
Normal file
4
ios/app/src/JitsiMeet-Bridging-Header.h
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
27
ios/app/src/RoutesHandler.h
Normal file
27
ios/app/src/RoutesHandler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// RoutesHandler.h
|
||||
// JitsiMeet
|
||||
//
|
||||
// Created by Alex Bumbu on 19.10.2022.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol RouteObserving <NSObject>
|
||||
|
||||
@property (nonatomic, readonly) void (^didRouteCallback)(NSString *route);
|
||||
|
||||
@end
|
||||
|
||||
@interface RoutesHandler : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
- (void)registerObserver:(id<RouteObserving>)observer forRoute:(NSString *)route;
|
||||
- (void)unregisterObserver:(id<RouteObserving>)observer;
|
||||
- (BOOL)routeURL:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
123
ios/app/src/RoutesHandler.m
Normal file
123
ios/app/src/RoutesHandler.m
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// RoutesHandler.m
|
||||
// JitsiMeet
|
||||
//
|
||||
// Created by Alex Bumbu on 19.10.2022.
|
||||
//
|
||||
|
||||
#import "RoutesHandler.h"
|
||||
|
||||
@protocol Routing <NSObject>
|
||||
|
||||
@property (nonatomic, readonly) NSString *route;
|
||||
@property (nonatomic, readonly) id<RouteObserving> observer;
|
||||
|
||||
@end
|
||||
|
||||
@interface Route: NSObject <Routing>
|
||||
|
||||
@property (nonatomic, readonly) NSString *route;
|
||||
@property (nonatomic, readonly, weak) id<RouteObserving> observer;
|
||||
|
||||
+ (instancetype)routeWithString:(nonnull NSString *)route observer:(id<RouteObserving>)observer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation RoutesHandler {
|
||||
NSMutableArray *routes;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static RoutesHandler *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
routes = [[NSMutableArray alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)registerObserver:(id<RouteObserving>)observer forRoute:(NSString *)route {
|
||||
[routes addObject:[Route routeWithString:route observer:observer]];
|
||||
}
|
||||
|
||||
- (void)unregisterObserver:(id<RouteObserving>)observer {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return evaluatedObject.observer == nil || evaluatedObject.observer == observer;
|
||||
}];
|
||||
NSArray *routesToClear = [routes filteredArrayUsingPredicate:predicate];
|
||||
[routes removeObjectsInArray:routesToClear];
|
||||
}
|
||||
|
||||
- (BOOL)routeURL:(NSURL *)url {
|
||||
[self clearRoutes];
|
||||
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:false];
|
||||
if (!components) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NSString *route = components.path;
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return [evaluatedObject.route isEqualToString:route];
|
||||
}];
|
||||
NSArray *routesToHandle = [routes filteredArrayUsingPredicate:predicate];
|
||||
if ([routesToHandle count] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (id<Routing> route in routesToHandle) {
|
||||
route.observer.didRouteCallback(route.route);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)clearRoutes {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id<Routing> _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return evaluatedObject.observer == nil;
|
||||
}];
|
||||
NSArray *routesToClear = [routes filteredArrayUsingPredicate:predicate];
|
||||
[routes removeObjectsInArray:routesToClear];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface Route()
|
||||
|
||||
@property (nonatomic, nonnull, copy) NSString *route;
|
||||
@property (nonatomic, weak) id<RouteObserving> observer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation Route
|
||||
|
||||
+ (instancetype)routeWithString:(nonnull NSString *)route observer:(nonnull id<RouteObserving>)observer {
|
||||
return [[Route alloc] initWithString:route observer:observer];
|
||||
}
|
||||
|
||||
- (instancetype)initWithString:(nonnull NSString *)route observer:(nonnull id<RouteObserving>)observer {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.route = route;
|
||||
self.observer = observer;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -21,8 +21,16 @@
|
||||
@import JitsiMeetSDK;
|
||||
|
||||
#import "Types.h"
|
||||
#import "RoutesHandler.h"
|
||||
#import "ViewController.h"
|
||||
#import "jitsi_meet-Swift.h"
|
||||
|
||||
@interface ViewController() <RouteObserving>
|
||||
|
||||
@property (nonatomic, nonnull, copy) void (^didRouteCallback)(NSString *);
|
||||
@property (nonatomic, assign) BOOL audioMuted;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
@@ -33,6 +41,8 @@
|
||||
view.delegate = self;
|
||||
|
||||
[view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
|
||||
|
||||
[self registerRouteObserver];
|
||||
}
|
||||
|
||||
// JitsiMeetViewDelegate
|
||||
@@ -53,6 +63,10 @@
|
||||
|
||||
- (void)conferenceJoined:(NSDictionary *)data {
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
|
||||
|
||||
self.audioMuted = [[data objectForKey:@"isAudioMuted"] boolValue];
|
||||
|
||||
[self refreshWidgetState:self.audioMuted];
|
||||
|
||||
// Register a NSUserActivity for this conference so it can be invoked as a
|
||||
// Siri shortcut.
|
||||
@@ -82,6 +96,12 @@
|
||||
|
||||
- (void)conferenceTerminated:(NSDictionary *)data {
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_TERMINATED" withData:data];
|
||||
|
||||
NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.org.jitsi.meet.appgroup"];
|
||||
NSURL *widgetStateFileURL = [sharedContainer URLByAppendingPathComponent:@"widgetState"];
|
||||
[[NSFileManager defaultManager] removeItemAtURL:widgetStateFileURL error:nil];
|
||||
|
||||
[WidgetKitHelper reloadAllWidgets];
|
||||
}
|
||||
|
||||
- (void)conferenceWillJoin:(NSDictionary *)data {
|
||||
@@ -107,7 +127,16 @@
|
||||
}
|
||||
|
||||
- (void)audioMutedChanged:(NSDictionary *)data {
|
||||
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
|
||||
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
|
||||
// CFNotificationCenterRef notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
|
||||
// CFNotificationCenterPostNotification(notificationCenter,
|
||||
// (__bridge CFStringRef)@"iOS_MeetingMutedChanged",
|
||||
// NULL,
|
||||
// NULL,
|
||||
// true);
|
||||
|
||||
self.audioMuted = [[data objectForKey:@"muted"] boolValue];
|
||||
[self refreshWidgetState:self.audioMuted];
|
||||
}
|
||||
|
||||
- (void)endpointTextMessageReceived:(NSDictionary *)data {
|
||||
@@ -132,9 +161,40 @@
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (void)registerRouteObserver {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
__weak JitsiMeetView *view = (JitsiMeetView *)self.view;
|
||||
self.didRouteCallback = ^(NSString *route) {
|
||||
if ([route isEqual:@"meet/toggleAudioMute"]) {
|
||||
weakSelf.audioMuted = !weakSelf.audioMuted;
|
||||
[view setAudioMuted:weakSelf.audioMuted];
|
||||
} else if ([route isEqualToString:@"meet/leaveMeeting"]) {
|
||||
[weakSelf terminate];
|
||||
}
|
||||
};
|
||||
|
||||
[[RoutesHandler sharedInstance] registerObserver:self forRoute:@"meet/toggleAudioMute"];
|
||||
[[RoutesHandler sharedInstance] registerObserver:self forRoute:@"meet/leaveMeeting"];
|
||||
}
|
||||
|
||||
- (void)terminate {
|
||||
JitsiMeetView *view = (JitsiMeetView *) self.view;
|
||||
[view leave];
|
||||
}
|
||||
|
||||
- (void)refreshWidgetState:(BOOL)audioMuted {
|
||||
// let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
|
||||
// return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
|
||||
|
||||
NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.org.jitsi.meet.appgroup"];
|
||||
NSURL *widgetStateFileURL = [sharedContainer URLByAppendingPathComponent:@"widgetState"];
|
||||
|
||||
NSDictionary *meetingState = @{@"audioMuted": @(audioMuted)};
|
||||
if (![meetingState writeToURL:widgetStateFileURL atomically:true]) {
|
||||
NSLog(@"error saving state file");
|
||||
}
|
||||
|
||||
[WidgetKitHelper reloadAllWidgets];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
20
ios/app/src/WidgetKitHelper.swift
Normal file
20
ios/app/src/WidgetKitHelper.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// WidgetKitHelper.swift
|
||||
// JitsiMeet
|
||||
//
|
||||
// Created by Alex Bumbu on 21.10.2022.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
@objcMembers final class WidgetKitHelper: NSObject {
|
||||
|
||||
class func reloadAllWidgets(){
|
||||
|
||||
#if arch(arm64) || arch(i386) || arch(x86_64)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -57,8 +57,8 @@
|
||||
openURL:(NSURL *_Nonnull)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *_Nonnull)options;
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application
|
||||
supportedInterfaceOrientationsForWindow:(UIWindow *)window;
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *_Nonnull)application
|
||||
supportedInterfaceOrientationsForWindow:(UIWindow *_Nonnull)window;
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const AVATAR_CHECKED_URLS = new Map();
|
||||
/* eslint-disable arrow-body-style, no-unused-vars */
|
||||
const AVATAR_CHECKER_FUNCTIONS = [
|
||||
(participant: IParticipant) => {
|
||||
return isJigasiParticipant(participant) ? JIGASI_PARTICIPANT_ICON : null;
|
||||
return participant?.isJigasi ? JIGASI_PARTICIPANT_ICON : null;
|
||||
},
|
||||
(participant: IParticipant) => {
|
||||
return isWhiteboardParticipant(participant) ? WHITEBOARD_PARTICIPANT_ICON : null;
|
||||
@@ -281,16 +281,6 @@ export function getFakeParticipants(stateful: IStateful) {
|
||||
return toState(stateful)['features/base/participants'].fakeParticipants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the fake participant is Jigasi.
|
||||
*
|
||||
* @param {IParticipant|undefined} participant - The participant entity.
|
||||
* @returns {boolean} - True if it's a Jigasi participant.
|
||||
*/
|
||||
function isJigasiParticipant(participant?: IParticipant): boolean {
|
||||
return participant?.fakeParticipant === FakeParticipant.Jigasi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the fake participant is a local screenshare.
|
||||
*
|
||||
|
||||
@@ -80,7 +80,7 @@ import {
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import { PARTICIPANT_JOINED_FILE, PARTICIPANT_LEFT_FILE } from './sounds';
|
||||
import { FakeParticipant, IJitsiParticipant } from './types';
|
||||
import { IJitsiParticipant } from './types';
|
||||
|
||||
import './subscriber';
|
||||
|
||||
@@ -438,7 +438,7 @@ StateListenerRegistry.register(
|
||||
store.dispatch(participantUpdated({
|
||||
conference,
|
||||
id: participant.getId(),
|
||||
fakeParticipant: value ? FakeParticipant.Jigasi : undefined
|
||||
isJigasi: value
|
||||
})),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'features_screen-sharing': (participant: IJitsiParticipant, value: string) =>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export enum FakeParticipant {
|
||||
Jigasi = 'Jigasi',
|
||||
LocalScreenShare = 'LocalScreenShare',
|
||||
RemoteScreenShare = 'RemoteScreenShare',
|
||||
SharedVideo = 'SharedVideo',
|
||||
@@ -21,6 +20,7 @@ export interface IParticipant {
|
||||
};
|
||||
getId?: Function;
|
||||
id: string;
|
||||
isJigasi?: boolean;
|
||||
isReplaced?: boolean;
|
||||
isReplacing?: number;
|
||||
jwtId?: string;
|
||||
|
||||
@@ -136,6 +136,10 @@ class StateListenerRegistry {
|
||||
* @returns {void}
|
||||
*/
|
||||
register(selector: Selector, listener: Listener, options?: RegistrationOptions) {
|
||||
if (typeof selector !== 'function' || typeof listener !== 'function') {
|
||||
throw new Error('Invalid selector or listener!');
|
||||
}
|
||||
|
||||
this._selectorListeners.add({
|
||||
listener,
|
||||
selector,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* IMPORTANT: this file is deprecated. All of these colors should be moved to
|
||||
* the theme instead.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The application's definition of the default color black.
|
||||
*/
|
||||
@@ -18,29 +23,12 @@ export const ColorPalette = {
|
||||
* the sake of consistency.
|
||||
*/
|
||||
black: BLACK,
|
||||
blackBlue: 'rgb(0, 3, 6)',
|
||||
blue: '#17A0DB',
|
||||
blueHighlight: '#1081b2',
|
||||
buttonUnderlay: '#495258',
|
||||
darkGrey: '#555555',
|
||||
darkBackground: 'rgb(19,21,25)',
|
||||
green: '#40b183',
|
||||
lightGrey: '#AAAAAA',
|
||||
overflowMenuItemUnderlay: '#EEEEEE',
|
||||
red: '#D00000',
|
||||
transparent: 'rgba(0, 0, 0, 0)',
|
||||
toggled: 'rgba(255,255,255,.15)',
|
||||
warning: 'rgb(215, 121, 118)',
|
||||
white: '#FFFFFF',
|
||||
|
||||
/**
|
||||
* These are colors from the atlaskit to be used on mobile, when needed.
|
||||
*
|
||||
* FIXME: Maybe a better solution would be good, or a native packaging of
|
||||
* the respective atlaskit components.
|
||||
*/
|
||||
G400: '#00875A', // Slime
|
||||
N500: '#42526E', // McFanning
|
||||
R400: '#DE350B', // Red dirt
|
||||
Y200: '#FFC400' // Pub mix
|
||||
white: '#FFFFFF'
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
import { statsEmitter } from '../../../connection-indicator';
|
||||
import statsEmitter from '../../../connection-indicator/statsEmitter';
|
||||
import { getLocalParticipant } from '../../participants';
|
||||
import { connect } from '../../redux';
|
||||
import { isTestModeEnabled } from '../functions';
|
||||
|
||||
@@ -190,6 +190,7 @@ interface IDialogProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
description?: string;
|
||||
disableAutoHideOnSubmit?: boolean;
|
||||
disableBackdropClose?: boolean;
|
||||
disableEnter?: boolean;
|
||||
hideCloseButton?: boolean;
|
||||
@@ -211,6 +212,7 @@ const Dialog = ({
|
||||
children,
|
||||
className,
|
||||
description,
|
||||
disableAutoHideOnSubmit = false,
|
||||
disableBackdropClose,
|
||||
hideCloseButton,
|
||||
disableEnter,
|
||||
@@ -232,7 +234,7 @@ const Dialog = ({
|
||||
}, [ onCancel ]);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
dispatch(hideDialog());
|
||||
!disableAutoHideOnSubmit && dispatch(hideDialog());
|
||||
onSubmit?.();
|
||||
}, [ onSubmit ]);
|
||||
|
||||
|
||||
@@ -2,18 +2,29 @@ import React, { ReactElement, useEffect, useState } from 'react';
|
||||
|
||||
export const DialogTransitionContext = React.createContext({ isUnmounting: false });
|
||||
|
||||
type TimeoutType = ReturnType<typeof setTimeout>;
|
||||
|
||||
const DialogTransition = ({ children }: { children: ReactElement | null; }) => {
|
||||
const [ childrenToRender, setChildrenToRender ] = useState(children);
|
||||
const [ isUnmounting, setIsUnmounting ] = useState(false);
|
||||
const [ timeoutID, setTimeoutID ] = useState<TimeoutType | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (children === null) {
|
||||
setIsUnmounting(true);
|
||||
setTimeout(() => {
|
||||
setChildrenToRender(children);
|
||||
setIsUnmounting(false);
|
||||
}, 150);
|
||||
if (typeof timeoutID === 'undefined') {
|
||||
setTimeoutID(setTimeout(() => {
|
||||
setChildrenToRender(children);
|
||||
setIsUnmounting(false);
|
||||
setTimeoutID(undefined);
|
||||
}, 150));
|
||||
}
|
||||
} else {
|
||||
if (typeof timeoutID !== 'undefined') {
|
||||
clearTimeout(timeoutID);
|
||||
setTimeoutID(undefined);
|
||||
setIsUnmounting(false);
|
||||
}
|
||||
setChildrenToRender(children);
|
||||
}
|
||||
}, [ children ]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { StyleProp, Text, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -57,7 +58,9 @@ const TitleBar = (props: IProps): JSX.Element => {
|
||||
<VideoQualityLabel />
|
||||
</View>
|
||||
<ConnectionIndicator
|
||||
// @ts-ignore
|
||||
iconStyle = { styles.connectionIndicatorIcon }
|
||||
// @ts-ignore
|
||||
participantId = { localParticipantId } />
|
||||
<View style = { styles.headerLabels as StyleProp<ViewStyle> }>
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
|
||||
@@ -209,7 +209,7 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
||||
*/
|
||||
export function mapStateToProps(state: Object) {
|
||||
return {
|
||||
_autoHideTimeout: state['features/base/config'].connectionIndicators.autoHideTimeout ?? defaultAutoHideTimeout
|
||||
_autoHideTimeout: state['features/base/config'].connectionIndicators?.autoHideTimeout ?? defaultAutoHideTimeout
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export * from './native';
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export * from './web';
|
||||
@@ -1,68 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { IconConnection } from '../../../base/icons';
|
||||
import { BaseIndicator } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import indicatorStyles from '../../../filmstrip/components/native/styles';
|
||||
import AbstractConnectionIndicator, {
|
||||
type Props,
|
||||
type State
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
import { CONNECTOR_INDICATOR_COLORS, iconStyle } from './styles';
|
||||
|
||||
/**
|
||||
* Implements an indicator to show the quality of the connection of a participant.
|
||||
*/
|
||||
class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
||||
/**
|
||||
* Initializes a new {@code ConnectionIndicator} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
autoHideTimeout: undefined,
|
||||
showIndicator: false,
|
||||
stats: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { showIndicator, stats } = this.state;
|
||||
const { percent } = stats;
|
||||
|
||||
if (!showIndicator || typeof percent === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Signal level on a scale 0..2
|
||||
const signalLevel = Math.floor(percent / 33.4);
|
||||
|
||||
return (
|
||||
<View
|
||||
style = {{
|
||||
...indicatorStyles.indicatorContainer,
|
||||
backgroundColor: CONNECTOR_INDICATOR_COLORS[signalLevel]
|
||||
}}>
|
||||
<BaseIndicator
|
||||
icon = { IconConnection }
|
||||
iconStyle = { this.props.iconStyle || iconStyle } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect()(ConnectionIndicator);
|
||||
@@ -0,0 +1,216 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { IconConnection } from '../../../base/icons/svg';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantById,
|
||||
isScreenShareParticipant
|
||||
} from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant
|
||||
} from '../../../base/tracks/functions.native';
|
||||
// @ts-ignore
|
||||
import indicatorStyles from '../../../filmstrip/components/native/styles';
|
||||
import {
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../functions';
|
||||
import AbstractConnectionIndicator, {
|
||||
type Props as AbstractProps,
|
||||
mapStateToProps as _abstractMapStateToProps
|
||||
// @ts-ignore
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
import {
|
||||
CONNECTOR_INDICATOR_COLORS,
|
||||
CONNECTOR_INDICATOR_LOST,
|
||||
CONNECTOR_INDICATOR_OTHER,
|
||||
iconStyle
|
||||
} from './styles';
|
||||
|
||||
type IProps = AbstractProps & {
|
||||
|
||||
/**
|
||||
* Whether connection indicators are disabled or not.
|
||||
*/
|
||||
_connectionIndicatorDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the inactive connection indicator is disabled or not.
|
||||
*/
|
||||
_connectionIndicatorInactiveDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection is inactive or not.
|
||||
*/
|
||||
_isConnectionStatusInactive: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection is interrupted or not.
|
||||
*/
|
||||
_isConnectionStatusInterrupted: boolean;
|
||||
|
||||
/**
|
||||
* Whether the current participant is a virtual screenshare.
|
||||
*/
|
||||
_isVirtualScreenshareParticipant: boolean;
|
||||
|
||||
/**
|
||||
* Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* Icon style override.
|
||||
*/
|
||||
iconStyle: any;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
autoHideTimeout: number | undefined;
|
||||
showIndicator: boolean;
|
||||
stats: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements an indicator to show the quality of the connection of a participant.
|
||||
*/
|
||||
class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new {@code ConnectionIndicator} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// @ts-ignore
|
||||
this.state = {
|
||||
autoHideTimeout: undefined,
|
||||
showIndicator: false,
|
||||
stats: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon configuration from CONNECTOR_INDICATOR_COLORS which has a percentage
|
||||
* that matches or exceeds the passed in percentage. The implementation
|
||||
* assumes CONNECTOR_INDICATOR_COLORS is already sorted by highest to lowest
|
||||
* percentage.
|
||||
*
|
||||
* @param {number} percent - The connection percentage, out of 100, to find
|
||||
* the closest matching configuration for.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDisplayConfiguration(percent: number): any {
|
||||
return CONNECTOR_INDICATOR_COLORS.find(x => percent >= x.percent) || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_connectionIndicatorInactiveDisabled,
|
||||
_connectionIndicatorDisabled,
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
// @ts-ignore
|
||||
} = this.props;
|
||||
const {
|
||||
showIndicator,
|
||||
stats
|
||||
// @ts-ignore
|
||||
} = this.state;
|
||||
const { percent } = stats;
|
||||
|
||||
if (!showIndicator || typeof percent === 'undefined'
|
||||
|| _connectionIndicatorDisabled || _isVirtualScreenshareParticipant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let indicatorColor;
|
||||
|
||||
if (_isConnectionStatusInactive) {
|
||||
if (_connectionIndicatorInactiveDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
indicatorColor = CONNECTOR_INDICATOR_OTHER;
|
||||
} else if (_isConnectionStatusInterrupted) {
|
||||
indicatorColor = CONNECTOR_INDICATOR_LOST;
|
||||
} else {
|
||||
const displayConfig = this._getDisplayConfiguration(percent);
|
||||
|
||||
if (!displayConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
indicatorColor = displayConfig.color;
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style = {{
|
||||
...indicatorStyles.indicatorContainer,
|
||||
backgroundColor: indicatorColor
|
||||
}}>
|
||||
<BaseIndicator
|
||||
icon = { IconConnection }
|
||||
// @ts-ignore
|
||||
iconStyle = { this.props.iconStyle || iconStyle } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {IProps} ownProps - The own props of the component.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const { participantId } = ownProps;
|
||||
const tracks = state['features/base/tracks'];
|
||||
const participant = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
const _isVirtualScreenshareParticipant = isScreenShareParticipant(participant);
|
||||
let _isConnectionStatusInactive;
|
||||
let _isConnectionStatusInterrupted;
|
||||
|
||||
if (!_isVirtualScreenshareParticipant) {
|
||||
const _videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
|
||||
_isConnectionStatusInactive = isTrackStreamingStatusInactive(_videoTrack);
|
||||
_isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
}
|
||||
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_connectionIndicatorDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.disabled),
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default connect(_mapStateToProps)(ConnectionIndicator);
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export { default as ConnectionIndicator } from './ConnectionIndicator';
|
||||
@@ -1,13 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../../base/styles';
|
||||
|
||||
export const CONNECTOR_INDICATOR_COLORS = [
|
||||
ColorPalette.red,
|
||||
ColorPalette.Y200,
|
||||
ColorPalette.green
|
||||
];
|
||||
|
||||
export const iconStyle = {
|
||||
fontSize: 14
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
// @ts-ignore
|
||||
import { INDICATOR_DISPLAY_THRESHOLD } from '../AbstractConnectionIndicator';
|
||||
|
||||
export const CONNECTOR_INDICATOR_LOST = BaseTheme.palette.ui05;
|
||||
export const CONNECTOR_INDICATOR_OTHER = BaseTheme.palette.action01;
|
||||
export const CONNECTOR_INDICATOR_COLORS = [
|
||||
|
||||
// Full (3 bars)
|
||||
{
|
||||
color: BaseTheme.palette.success01,
|
||||
percent: INDICATOR_DISPLAY_THRESHOLD
|
||||
},
|
||||
|
||||
// 2 bars.
|
||||
{
|
||||
color: BaseTheme.palette.warning01,
|
||||
percent: 10
|
||||
},
|
||||
|
||||
// 1 bar.
|
||||
{
|
||||
color: BaseTheme.palette.iconError,
|
||||
percent: 0
|
||||
}
|
||||
];
|
||||
|
||||
export const iconStyle = {
|
||||
fontSize: 14
|
||||
};
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
import AbstractConnectionIndicator, {
|
||||
type Props as AbstractProps,
|
||||
type State as AbstractState,
|
||||
INDICATOR_DISPLAY_THRESHOLD
|
||||
INDICATOR_DISPLAY_THRESHOLD,
|
||||
mapStateToProps as _abstractMapStateToProps
|
||||
// @ts-ignore
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
@@ -386,6 +387,7 @@ export function _mapStateToProps(state: IReduxState, ownProps: Props) {
|
||||
const _isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_isVirtualScreenshareParticipant: isScreenShareParticipant(participant),
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export * from './components';
|
||||
|
||||
export { default as statsEmitter } from './statsEmitter';
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
getVideoTrackByParticipant,
|
||||
trackStreamingStatusChanged
|
||||
} from '../../../base/tracks';
|
||||
import { ConnectionIndicator } from '../../../connection-indicator';
|
||||
import ConnectionIndicator from '../../../connection-indicator/components/native/ConnectionIndicator';
|
||||
import { DisplayNameLabel } from '../../../display-name';
|
||||
import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions';
|
||||
import {
|
||||
@@ -191,6 +191,8 @@ class Thumbnail extends PureComponent<Props> {
|
||||
dispatch(showSharedVideoMenu(_participantId));
|
||||
}
|
||||
|
||||
// TODO: add support for getting info about the virtual screen shares.
|
||||
|
||||
if (!_fakeParticipant) {
|
||||
dispatch(showContextMenuDetails(_participantId, _local));
|
||||
}
|
||||
|
||||
@@ -85,6 +85,16 @@ export function getActiveParticipantsIds(_state: any) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Not implemented on mobile.
|
||||
*
|
||||
* @param {any} _state - Redux state.
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
export function getPinnedActiveParticipants(_state: any) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of participants displayed in tile view.
|
||||
*
|
||||
|
||||
@@ -183,7 +183,7 @@ function AddPeopleDialog({
|
||||
&& <DialInSection phoneNumber = { _phoneNumber } />
|
||||
}
|
||||
{
|
||||
!_dialInVisible && _isVpaasMeeting && <DialInLimit />
|
||||
!_phoneNumber && _dialInVisible && _isVpaasMeeting && <DialInLimit />
|
||||
}
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
@@ -143,7 +143,7 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
|
||||
return (
|
||||
<JitsiScreen
|
||||
addBottomPadding = { false }
|
||||
safeAreaInsets = { [ 'left' ] }
|
||||
safeAreaInsets = { [ 'left', 'right' ] }
|
||||
style = { contentWrapperStyles }>
|
||||
<BrandingImageBackground />
|
||||
{
|
||||
|
||||
@@ -3,11 +3,12 @@ import BaseTheme from '../../base/ui/components/BaseTheme.native';
|
||||
|
||||
export default {
|
||||
joinButton: {
|
||||
marginVertical: BaseTheme.spacing[3]
|
||||
marginTop: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
joinLowBandwidthLabel: {
|
||||
color: BaseTheme.palette.text01,
|
||||
marginTop: BaseTheme.spacing[3],
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
@@ -76,7 +77,7 @@ export default {
|
||||
flexDirection: 'row',
|
||||
height: 60,
|
||||
justifyContent: 'space-between',
|
||||
marginVertical: BaseTheme.spacing[3],
|
||||
marginBottom: BaseTheme.spacing[3],
|
||||
paddingHorizontal: BaseTheme.spacing[2],
|
||||
width: 148
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { hideDialog } from '../../base/dialog/actions';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getParticipantById } from '../../base/participants';
|
||||
import { connect } from '../../base/redux';
|
||||
@@ -138,7 +137,6 @@ class RemoteControlAuthorizationDialog extends Component<Props> {
|
||||
_onSubmit() {
|
||||
const { dispatch, participantId } = this.props;
|
||||
|
||||
dispatch(hideDialog());
|
||||
dispatch(grant(participantId));
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { hideDialog } from '../../../base/dialog/actions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||
@@ -52,7 +53,9 @@ class SharedVideoDialog extends AbstractSharedVideoDialog<any> {
|
||||
_onSubmitValue() {
|
||||
const result = super._onSetVideoLink(this.state.value);
|
||||
|
||||
if (!result) {
|
||||
if (result) {
|
||||
this.props.dispatch(hideDialog());
|
||||
} else {
|
||||
this.setState({
|
||||
error: true
|
||||
});
|
||||
@@ -72,6 +75,7 @@ class SharedVideoDialog extends AbstractSharedVideoDialog<any> {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableAutoHideOnSubmit = { true }
|
||||
ok = {{
|
||||
disabled: this.state.okDisabled,
|
||||
translationKey: 'dialog.Share'
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
import React, { Component } from 'react';
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { withTheme } from 'react-native-paper';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { BottomSheet, hideSheet } from '../../../base/dialog';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
// @ts-ignore
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
// @ts-ignore
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
// @ts-ignore
|
||||
import { bottomSheetStyles } from '../../../base/dialog/components/native/styles';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons';
|
||||
import { getParticipantDisplayName } from '../../../base/participants';
|
||||
import { BaseIndicator } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons/svg';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import { getParticipantDisplayName } from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant
|
||||
} from '../../../base/tracks/functions.native';
|
||||
import {
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../../connection-indicator/functions';
|
||||
import statsEmitter from '../../../connection-indicator/statsEmitter';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -20,62 +37,87 @@ import styles from './styles';
|
||||
const AVATAR_SIZE = 25;
|
||||
|
||||
const CONNECTION_QUALITY = [
|
||||
'Low',
|
||||
'Medium',
|
||||
'Good'
|
||||
|
||||
// Full (3 bars)
|
||||
{
|
||||
msg: 'connectionindicator.quality.good',
|
||||
percent: 30 // INDICATOR_DISPLAY_THRESHOLD
|
||||
},
|
||||
|
||||
// 2 bars.
|
||||
{
|
||||
msg: 'connectionindicator.quality.nonoptimal',
|
||||
percent: 10
|
||||
},
|
||||
|
||||
// 1 bar.
|
||||
{
|
||||
msg: 'connectionindicator.quality.poor',
|
||||
percent: 0
|
||||
}
|
||||
];
|
||||
|
||||
export type Props = {
|
||||
type IProps = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
* Whether this participant's connection is inactive.
|
||||
*/
|
||||
dispatch: Function,
|
||||
_isConnectionStatusInactive: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
* Whether this participant's connection is interrupted.
|
||||
*/
|
||||
participantID: string,
|
||||
_isConnectionStatusInterrupted: boolean;
|
||||
|
||||
/**
|
||||
* True if the menu is currently open, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
_isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
_participantDisplayName: string;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
*/
|
||||
participantID: string;
|
||||
|
||||
/**
|
||||
* The function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function,
|
||||
t: Function;
|
||||
|
||||
/**
|
||||
* Theme used for styles.
|
||||
*/
|
||||
theme: Object
|
||||
}
|
||||
theme: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link ConnectionStatusComponent}.
|
||||
*/
|
||||
type State = {
|
||||
resolutionString: string,
|
||||
downloadString: string,
|
||||
uploadString: string,
|
||||
packetLostDownloadString: string,
|
||||
packetLostUploadString: string,
|
||||
serverRegionString: string,
|
||||
codecString: string,
|
||||
connectionString: string
|
||||
type IState = {
|
||||
codecString: string;
|
||||
connectionString: string;
|
||||
downloadString: string;
|
||||
packetLostDownloadString: string;
|
||||
packetLostUploadString: string;
|
||||
resolutionString: string;
|
||||
serverRegionString: string;
|
||||
uploadString: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that show the connection statistics.
|
||||
*/
|
||||
class ConnectionStatusComponent extends Component<Props, State> {
|
||||
class ConnectionStatusComponent extends PureComponent<IProps, IState> {
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
@@ -85,7 +127,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onStatsUpdated = this._onStatsUpdated.bind(this);
|
||||
@@ -121,15 +163,15 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
<View style = { styles.statsWrapper }>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.status')} ` }
|
||||
{ t('connectionindicator.status') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.connectionString }
|
||||
{ t(this.state.connectionString) }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.bitrate')}` }
|
||||
{ t('connectionindicator.bitrate') }
|
||||
</Text>
|
||||
<BaseIndicator
|
||||
icon = { IconArrowDownLarge }
|
||||
@@ -150,7 +192,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.packetloss')}` }
|
||||
{ t('connectionindicator.packetloss') }
|
||||
</Text>
|
||||
<BaseIndicator
|
||||
icon = { IconArrowDownLarge }
|
||||
@@ -171,7 +213,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.resolution')} ` }
|
||||
{ t('connectionindicator.resolution') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.resolutionString }
|
||||
@@ -179,7 +221,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.codecs')}` }
|
||||
{ t('connectionindicator.codecs') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.codecString }
|
||||
@@ -206,7 +248,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @inheritdoc
|
||||
* returns {void}
|
||||
*/
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
if (prevProps.participantID !== this.props.participantID) {
|
||||
statsEmitter.unsubscribeToClientStats(
|
||||
prevProps.participantID, this._onStatsUpdated);
|
||||
@@ -215,8 +257,6 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onStatsUpdated: Object => void;
|
||||
|
||||
/**
|
||||
* Callback invoked when new connection stats associated with the passed in
|
||||
* user ID are available. Will update the component's display of current
|
||||
@@ -239,9 +279,8 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {State}
|
||||
*/
|
||||
_buildState(stats) {
|
||||
_buildState(stats: any) {
|
||||
const { download: downloadBitrate, upload: uploadBitrate } = this._extractBitrate(stats) ?? {};
|
||||
|
||||
const { download: downloadPacketLost, upload: uploadPacketLost } = this._extractPacketLost(stats) ?? {};
|
||||
|
||||
return {
|
||||
@@ -254,7 +293,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
? this.state.packetLostUploadString : `${uploadPacketLost}%`,
|
||||
serverRegionString: this._extractServer(stats) ?? this.state.serverRegionString,
|
||||
codecString: this._extractCodecs(stats) ?? this.state.codecString,
|
||||
connectionString: this._extractConnection(stats) ?? this.state.connectionString
|
||||
connectionString: this._extractConnection(stats)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -265,7 +304,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractResolutionString(stats) {
|
||||
_extractResolutionString(stats: any) {
|
||||
const { framerate, resolution } = stats;
|
||||
|
||||
const resolutionString = Object.keys(resolution || {})
|
||||
@@ -290,7 +329,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {{ download, upload }}
|
||||
*/
|
||||
_extractBitrate(stats) {
|
||||
_extractBitrate(stats: any) {
|
||||
return stats.bitrate;
|
||||
}
|
||||
|
||||
@@ -301,7 +340,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {{ download, upload }}
|
||||
*/
|
||||
_extractPacketLost(stats) {
|
||||
_extractPacketLost(stats: any) {
|
||||
return stats.packetLoss;
|
||||
}
|
||||
|
||||
@@ -312,7 +351,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractServer(stats) {
|
||||
_extractServer(stats: any) {
|
||||
return stats.serverRegion;
|
||||
}
|
||||
|
||||
@@ -323,17 +362,17 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractCodecs(stats) {
|
||||
_extractCodecs(stats: any) {
|
||||
const { codec } = stats;
|
||||
|
||||
let codecString;
|
||||
|
||||
if (codec) {
|
||||
const audioCodecs = Object.values(codec)
|
||||
.map(c => c.audio)
|
||||
.map((c: any) => c.audio)
|
||||
.filter(Boolean);
|
||||
const videoCodecs = Object.values(codec)
|
||||
.map(c => c.video)
|
||||
.map((c: any) => c.video)
|
||||
.filter(Boolean);
|
||||
|
||||
if (audioCodecs.length || videoCodecs.length) {
|
||||
@@ -352,14 +391,39 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractConnection(stats) {
|
||||
_extractConnection(stats: any) {
|
||||
const { connectionQuality } = stats;
|
||||
const {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
} = this.props;
|
||||
|
||||
if (connectionQuality) {
|
||||
const signalLevel = Math.floor(connectionQuality / 33.4);
|
||||
|
||||
return CONNECTION_QUALITY[signalLevel];
|
||||
if (_isConnectionStatusInactive) {
|
||||
return 'connectionindicator.quality.inactive';
|
||||
} else if (_isConnectionStatusInterrupted) {
|
||||
return 'connectionindicator.quality.lost';
|
||||
} else if (typeof connectionQuality === 'undefined') {
|
||||
return 'connectionindicator.quality.good';
|
||||
}
|
||||
|
||||
const qualityConfig = this._getQualityConfig(connectionQuality);
|
||||
|
||||
return qualityConfig.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quality configuration from CONNECTION_QUALITY which has a percentage
|
||||
* that matches or exceeds the passed in percentage. The implementation
|
||||
* assumes CONNECTION_QUALITY is already sorted by highest to lowest
|
||||
* percentage.
|
||||
*
|
||||
* @param {number} percent - The connection percentage, out of 100, to find
|
||||
* the closest matching configuration for.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getQualityConfig(percent: number): any {
|
||||
return CONNECTION_QUALITY.find(x => percent >= x.percent) || {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,12 +470,19 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const { participantID } = ownProps;
|
||||
const tracks = state['features/base/tracks'];
|
||||
const _videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantID);
|
||||
const _isConnectionStatusInactive = isTrackStreamingStatusInactive(_videoTrack);
|
||||
const _isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
|
||||
return {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_participantDisplayName: getParticipantDisplayName(state, participantID)
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default translate(connect(_mapStateToProps)(withTheme(ConnectionStatusComponent)));
|
||||
Reference in New Issue
Block a user