forked from mirrors/linphone-iphone
Add participant list fragment to the call view
This commit is contained in:
parent
582c1b1d66
commit
0a2d4a1682
11 changed files with 463 additions and 86 deletions
|
|
@ -43,6 +43,7 @@
|
|||
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071D2AC5922E0037746F /* ColorExtension.swift */; };
|
||||
D71707202AC5989C0037746F /* TextExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071F2AC5989C0037746F /* TextExtension.swift */; };
|
||||
D7173EBE2B7A5C0A00BCC481 /* LinphoneUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7173EBD2B7A5C0A00BCC481 /* LinphoneUtils.swift */; };
|
||||
D717630D2BD7BD0E00464097 /* ParticipantsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717630C2BD7BD0E00464097 /* ParticipantsListFragment.swift */; };
|
||||
D71968922B86369D00DF4459 /* ChatBubbleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71968912B86369D00DF4459 /* ChatBubbleView.swift */; };
|
||||
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */; };
|
||||
D719ABB92ABC67BF00B41C10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABB82ABC67BF00B41C10 /* ContentView.swift */; };
|
||||
|
|
@ -191,6 +192,7 @@
|
|||
D717071D2AC5922E0037746F /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = "<group>"; };
|
||||
D717071F2AC5989C0037746F /* TextExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextExtension.swift; sourceTree = "<group>"; };
|
||||
D7173EBD2B7A5C0A00BCC481 /* LinphoneUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinphoneUtils.swift; sourceTree = "<group>"; };
|
||||
D717630C2BD7BD0E00464097 /* ParticipantsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantsListFragment.swift; sourceTree = "<group>"; };
|
||||
D71968912B86369D00DF4459 /* ChatBubbleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatBubbleView.swift; sourceTree = "<group>"; };
|
||||
D719ABB32ABC67BF00B41C10 /* Linphone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Linphone.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinphoneApp.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -582,6 +584,7 @@
|
|||
children = (
|
||||
D75759312B56D40900E7AC10 /* ZRTPPopup.swift */,
|
||||
D7F4D9CA2B5FD27200CDCD76 /* CallsListFragment.swift */,
|
||||
D717630C2BD7BD0E00464097 /* ParticipantsListFragment.swift */,
|
||||
);
|
||||
path = Fragments;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -954,6 +957,7 @@
|
|||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */,
|
||||
66E50A492BD12B2300AD61CA /* MeetingsView.swift in Sources */,
|
||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */,
|
||||
D717630D2BD7BD0E00464097 /* ParticipantsListFragment.swift in Sources */,
|
||||
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */,
|
||||
D79622342B1DFE600037EACD /* DialerBottomSheet.swift in Sources */,
|
||||
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,16 @@
|
|||
},
|
||||
"%lld" : {
|
||||
|
||||
},
|
||||
"%lld %@" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "%1$lld %2$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld Book (Example)" : {
|
||||
"extractionState" : "manual",
|
||||
|
|
@ -167,6 +177,9 @@
|
|||
},
|
||||
"Add to favourites" : {
|
||||
|
||||
},
|
||||
"Administrateur" : {
|
||||
|
||||
},
|
||||
"All calls will be removed from the history." : {
|
||||
|
||||
|
|
@ -355,6 +368,9 @@
|
|||
},
|
||||
"Error Name" : {
|
||||
|
||||
},
|
||||
"Etes-vous sûr de vouloir supprimer %@ ?" : {
|
||||
|
||||
},
|
||||
"Favourites" : {
|
||||
|
||||
|
|
@ -509,6 +525,12 @@
|
|||
},
|
||||
"No meeting for the moment..." : {
|
||||
|
||||
},
|
||||
"No participant for the moment..." : {
|
||||
|
||||
},
|
||||
"Non" : {
|
||||
|
||||
},
|
||||
"Not account yet?" : {
|
||||
|
||||
|
|
@ -524,6 +546,9 @@
|
|||
},
|
||||
"Other actions" : {
|
||||
|
||||
},
|
||||
"Oui" : {
|
||||
|
||||
},
|
||||
"Partage d'écran" : {
|
||||
|
||||
|
|
@ -683,6 +708,9 @@
|
|||
},
|
||||
"Supprimer la conversation" : {
|
||||
|
||||
},
|
||||
"Supprimer un participant" : {
|
||||
|
||||
},
|
||||
"TCP" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ struct CallView: View {
|
|||
@State var displayVideo = false
|
||||
|
||||
@Binding var fullscreenVideo: Bool
|
||||
@Binding var isShowCallsListFragment: Bool
|
||||
@State var isShowCallsListFragment: Bool = false
|
||||
@State var isShowParticipantsListFragment: Bool = false
|
||||
@Binding var isShowStartCallFragment: Bool
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -107,8 +108,14 @@ struct CallView: View {
|
|||
|
||||
if isShowCallsListFragment {
|
||||
CallsListFragment(callViewModel: callViewModel, isShowCallsListFragment: $isShowCallsListFragment)
|
||||
.zIndex(4)
|
||||
.transition(.move(edge: .bottom))
|
||||
.zIndex(4)
|
||||
.transition(.move(edge: .bottom))
|
||||
}
|
||||
|
||||
if isShowParticipantsListFragment {
|
||||
ParticipantsListFragment(callViewModel: callViewModel, isShowParticipantsListFragment: $isShowParticipantsListFragment)
|
||||
.zIndex(4)
|
||||
.transition(.move(edge: .bottom))
|
||||
}
|
||||
|
||||
if callViewModel.zrtpPopupDisplayed == true {
|
||||
|
|
@ -1218,6 +1225,9 @@ struct CallView: View {
|
|||
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
isShowParticipantsListFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("users")
|
||||
|
|
@ -1458,58 +1468,110 @@ struct CallView: View {
|
|||
.frame(height: geo.size.height * 0.15)
|
||||
} else {
|
||||
HStack {
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
callViewModel.isTransferInsteadCall = true
|
||||
MagicSearchSingleton.shared.searchForSuggestions()
|
||||
isShowStartCallFragment.toggle()
|
||||
if callViewModel.isOneOneCall {
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
callViewModel.isTransferInsteadCall = true
|
||||
MagicSearchSingleton.shared.searchForSuggestions()
|
||||
isShowStartCallFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("phone-transfer")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("phone-transfer")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text(callViewModel.calls.count < 2 ? "Transfer" : "Attended transfer")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
MagicSearchSingleton.shared.searchForSuggestions()
|
||||
isShowStartCallFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("phone-plus")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("New call")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
} else {
|
||||
VStack {
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
HStack {
|
||||
Image("screencast")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.gray500)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(.white)
|
||||
.cornerRadius(40)
|
||||
.disabled(true)
|
||||
|
||||
Text("Partage d'écran")
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
|
||||
Text(callViewModel.calls.count < 2 ? "Transfer" : "Attended transfer")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
MagicSearchSingleton.shared.searchForSuggestions()
|
||||
isShowStartCallFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("phone-plus")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
VStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
isShowParticipantsListFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("users")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Participants")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle())
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("New call")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125)
|
||||
|
||||
VStack {
|
||||
ZStack {
|
||||
|
|
@ -1823,7 +1885,7 @@ struct PressedButtonStyle: ButtonStyle {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
CallView(callViewModel: CallViewModel(), fullscreenVideo: .constant(false), isShowCallsListFragment: .constant(false), isShowStartCallFragment: .constant(false))
|
||||
CallView(callViewModel: CallViewModel(), fullscreenVideo: .constant(false), isShowStartCallFragment: .constant(false))
|
||||
}
|
||||
// swiftlint:enable type_body_length
|
||||
// swiftlint:enable line_length
|
||||
|
|
|
|||
|
|
@ -1,18 +1,239 @@
|
|||
//
|
||||
// ParticipantsListFragment.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by Benoît Martins on 23/04/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ParticipantsListFragment: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
@ObservedObject private var contactsManager = ContactsManager.shared
|
||||
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
|
||||
@ObservedObject var callViewModel: CallViewModel
|
||||
|
||||
@State private var delayedColor = Color.white
|
||||
|
||||
@Binding var isShowParticipantsListFragment: Bool
|
||||
|
||||
@State private var isShowPopup = false
|
||||
@State private var indexToRemove = -1
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack(spacing: 1) {
|
||||
Rectangle()
|
||||
.foregroundColor(delayedColor)
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
.frame(height: 0)
|
||||
.task(delayColor)
|
||||
|
||||
HStack {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
.padding(.all, 10)
|
||||
.padding(.top, 2)
|
||||
.padding(.leading, -10)
|
||||
.onTapGesture {
|
||||
delayColorDismiss()
|
||||
withAnimation {
|
||||
isShowParticipantsListFragment.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Text("\(callViewModel.participantList.count + 1) \(callViewModel.participantList.isEmpty ? "Participant" : "Participants")")
|
||||
.multilineTextAlignment(.leading)
|
||||
.default_text_style_orange_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 4)
|
||||
.background(.white)
|
||||
|
||||
participantsList
|
||||
}
|
||||
.background(.white)
|
||||
|
||||
if self.isShowPopup {
|
||||
let contentPopup = Text("Etes-vous sûr de vouloir supprimer \(callViewModel.participantList[indexToRemove].name) ?")
|
||||
PopupView(isShowPopup: $isShowPopup,
|
||||
title: Text("Supprimer un participant"),
|
||||
content: contentPopup,
|
||||
titleFirstButton: Text("Non"),
|
||||
actionFirstButton: {self.isShowPopup.toggle()},
|
||||
titleSecondButton: Text("Oui"),
|
||||
actionSecondButton: {
|
||||
callViewModel.removeParticipant(index: indexToRemove)
|
||||
self.isShowPopup.toggle()
|
||||
indexToRemove = -1
|
||||
})
|
||||
.background(.black.opacity(0.65))
|
||||
.onTapGesture {
|
||||
self.isShowPopup.toggle()
|
||||
indexToRemove = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
|
||||
@Sendable private func delayColor() async {
|
||||
try? await Task.sleep(nanoseconds: 250_000_000)
|
||||
delayedColor = Color.orangeMain500
|
||||
}
|
||||
|
||||
func delayColorDismiss() {
|
||||
Task {
|
||||
try? await Task.sleep(nanoseconds: 80_000_000)
|
||||
delayedColor = .white
|
||||
}
|
||||
}
|
||||
|
||||
var participantsList: some View {
|
||||
VStack {
|
||||
List {
|
||||
HStack {
|
||||
HStack {
|
||||
if callViewModel.myParticipantModel != nil {
|
||||
Avatar(contactAvatarModel: callViewModel.myParticipantModel!.avatarModel, avatarSize: 50, hidePresence: true)
|
||||
|
||||
Text(callViewModel.myParticipantModel!.name)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
|
||||
Spacer()
|
||||
|
||||
if callViewModel.myParticipantModel!.isAdmin {
|
||||
Text("Administrateur")
|
||||
.foregroundStyle(Color.grayMain2c300)
|
||||
.default_text_style(styleSize: 12)
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
if callViewModel.myParticipantModel!.isAdmin {
|
||||
Toggle("", isOn: .constant(true))
|
||||
.tint(Color.greenSuccess700)
|
||||
.labelsHidden()
|
||||
.padding(.horizontal, 4)
|
||||
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
.foregroundStyle(Color.grayMain2c400)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
.frame(width: 30, height: 30, alignment: .center)
|
||||
.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.background(.white)
|
||||
|
||||
ForEach(0..<callViewModel.participantList.count, id: \.self) { index in
|
||||
HStack {
|
||||
HStack {
|
||||
if index < callViewModel.participantList.count {
|
||||
Avatar(contactAvatarModel: callViewModel.participantList[index].avatarModel, avatarSize: 50, hidePresence: true)
|
||||
|
||||
Text(callViewModel.participantList[index].name)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
|
||||
Spacer()
|
||||
|
||||
if callViewModel.participantList[index].isAdmin {
|
||||
Text("Administrateur")
|
||||
.foregroundStyle(Color.grayMain2c300)
|
||||
.default_text_style(styleSize: 12)
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
if callViewModel.myParticipantModel!.isAdmin {
|
||||
Toggle("", isOn: $callViewModel.participantList[index].isAdmin)
|
||||
.tint(Color.greenSuccess500)
|
||||
.labelsHidden()
|
||||
.padding(.horizontal, 10)
|
||||
.onTapGesture {
|
||||
callViewModel.toggleAdminParticipant(index: index)
|
||||
}
|
||||
|
||||
Button {
|
||||
indexToRemove = index
|
||||
isShowPopup.toggle()
|
||||
} label: {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
.foregroundStyle(Color.grayMain2c400)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
.frame(width: 30, height: 30, alignment: .center)
|
||||
.background(Color.grayMain2c100)
|
||||
.cornerRadius(50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.background(.white)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.overlay(
|
||||
VStack {
|
||||
if callViewModel.myParticipantModel == nil && callViewModel.calls.isEmpty {
|
||||
Spacer()
|
||||
Image("illus-belledonne")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.clipped()
|
||||
.padding(.all)
|
||||
Text("No participant for the moment...")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.all)
|
||||
)
|
||||
}
|
||||
.navigationTitle("")
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ParticipantsListFragment()
|
||||
ParticipantsListFragment(callViewModel: CallViewModel(), isShowParticipantsListFragment: .constant(true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ class ParticipantModel: ObservableObject {
|
|||
@Published var isJoining: Bool
|
||||
@Published var onPause: Bool
|
||||
@Published var isMuted: Bool
|
||||
@Published var isAdmin: Bool
|
||||
|
||||
init(address: Address, isJoining: Bool = false, onPause: Bool = false, isMuted: Bool = false) {
|
||||
init(address: Address, isJoining: Bool = false, onPause: Bool = false, isMuted: Bool = false, isAdmin: Bool = false) {
|
||||
self.address = address
|
||||
|
||||
self.sipUri = address.asStringUriOnly()
|
||||
|
|
@ -48,5 +49,6 @@ class ParticipantModel: ObservableObject {
|
|||
self.isJoining = isJoining
|
||||
self.onPause = onPause
|
||||
self.isMuted = isMuted
|
||||
self.isAdmin = isAdmin
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,9 +215,9 @@ class CallViewModel: ObservableObject {
|
|||
|
||||
var myParticipantModelTmp: ParticipantModel? = nil
|
||||
if conf.me?.address != nil {
|
||||
myParticipantModelTmp = ParticipantModel(address: conf.me!.address!, isJoining: false, onPause: false, isMuted: false)
|
||||
myParticipantModelTmp = ParticipantModel(address: conf.me!.address!, isJoining: false, onPause: false, isMuted: false, isAdmin: conf.me!.isAdmin)
|
||||
} else if self.currentCall?.callLog?.localAddress != nil {
|
||||
myParticipantModelTmp = ParticipantModel(address: self.currentCall!.callLog!.localAddress!, isJoining: false, onPause: false, isMuted: false)
|
||||
myParticipantModelTmp = ParticipantModel(address: self.currentCall!.callLog!.localAddress!, isJoining: false, onPause: false, isMuted: false, isAdmin: conf.me!.isAdmin)
|
||||
}
|
||||
|
||||
var activeSpeakerParticipantTmp: ParticipantModel? = nil
|
||||
|
|
@ -262,12 +262,14 @@ class CallViewModel: ObservableObject {
|
|||
conf.participantDeviceList.forEach({ participantDevice in
|
||||
if participantDevice.address != nil && !conf.isMe(uri: participantDevice.address!.clone()!) {
|
||||
if !conf.isMe(uri: participantDevice.address!.clone()!) {
|
||||
let isAdmin = conf.participantList.first(where: {$0.address!.equal(address2: participantDevice.address!.clone()!)})?.isAdmin
|
||||
participantListTmp.append(
|
||||
ParticipantModel(
|
||||
address: participantDevice.address!,
|
||||
isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting,
|
||||
onPause: participantDevice.state == .OnHold,
|
||||
isMuted: participantDevice.isMuted
|
||||
isMuted: participantDevice.isMuted,
|
||||
isAdmin: isAdmin ?? false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -336,12 +338,14 @@ class CallViewModel: ObservableObject {
|
|||
cbValue.conference.participantDeviceList.forEach({ participantDevice in
|
||||
if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
let isAdmin = cbValue.conference.participantList.first(where: {$0.address!.equal(address2: participantDevice.address!.clone()!)})?.isAdmin
|
||||
participantListTmp.append(
|
||||
ParticipantModel(
|
||||
address: participantDevice.address!,
|
||||
isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting,
|
||||
onPause: participantDevice.state == .OnHold,
|
||||
isMuted: participantDevice.isMuted
|
||||
isMuted: participantDevice.isMuted,
|
||||
isAdmin: isAdmin ?? false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -368,12 +372,14 @@ class CallViewModel: ObservableObject {
|
|||
cbValue.conference.participantDeviceList.forEach({ participantDevice in
|
||||
if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
let isAdmin = cbValue.conference.participantList.first(where: {$0.address!.equal(address2: participantDevice.address!.clone()!)})?.isAdmin
|
||||
participantListTmp.append(
|
||||
ParticipantModel(
|
||||
address: participantDevice.address!,
|
||||
isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting,
|
||||
onPause: participantDevice.state == .OnHold,
|
||||
isMuted: participantDevice.isMuted
|
||||
isMuted: participantDevice.isMuted,
|
||||
isAdmin: isAdmin ?? false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -394,12 +400,14 @@ class CallViewModel: ObservableObject {
|
|||
cbValue.conference.participantDeviceList.forEach({ participantDevice in
|
||||
if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) {
|
||||
let isAdmin = cbValue.conference.participantList.first(where: {$0.address!.equal(address2: participantDevice.address!.clone()!)})?.isAdmin
|
||||
participantListTmp.append(
|
||||
ParticipantModel(
|
||||
address: participantDevice.address!,
|
||||
isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting,
|
||||
onPause: participantDevice.state == .OnHold,
|
||||
isMuted: participantDevice.isMuted
|
||||
isMuted: participantDevice.isMuted,
|
||||
isAdmin: isAdmin ?? false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -467,6 +475,25 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
)
|
||||
|
||||
self.mConferenceSuscriptions.insert(
|
||||
self.currentCall?.conference?.publisher?.onParticipantAdminStatusChanged?.postOnMainQueue {(cbValue: (conference: Conference, participant: Participant)) in
|
||||
let isAdmin = cbValue.participant.isAdmin
|
||||
if self.myParticipantModel != nil && self.myParticipantModel!.address.clone()!.equal(address2: cbValue.participant.address!) {
|
||||
DispatchQueue.main.async {
|
||||
self.myParticipantModel!.isAdmin = isAdmin
|
||||
}
|
||||
} else {
|
||||
self.participantList.forEach({ participantDevice in
|
||||
if participantDevice.address.clone()!.equal(address2: cbValue.participant.address!) {
|
||||
DispatchQueue.main.async {
|
||||
participantDevice.isAdmin = isAdmin
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -830,5 +857,29 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toggleAdminParticipant(index: Int) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
self.currentCall?.conference?.participantList.forEach({ participant in
|
||||
if participant.address != nil && self.participantList[index].address.clone() != nil && participant.address!.equal(address2: self.participantList[index].address.clone()!) {
|
||||
self.currentCall?.conference?.setParticipantAdminStatus(participant: participant, isAdmin: !participant.isAdmin)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func removeParticipant(index: Int) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
self.currentCall?.conference?.participantList.forEach({ participant in
|
||||
if participant.address != nil && self.participantList[index].address.clone() != nil && participant.address!.equal(address2: self.participantList[index].address.clone()!) {
|
||||
do {
|
||||
try self.currentCall?.conference?.removeParticipant(participant: participant)
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// swiftlint:enable type_body_length
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ struct ContactsListFragment: View {
|
|||
.foregroundStyle(Color.orangeMain500)
|
||||
}
|
||||
}
|
||||
.frame(height: 50)
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
|
|
@ -91,8 +95,6 @@ struct ContactsListFragment: View {
|
|||
contactViewModel.selectedFriend = contactsManager.lastSearch[index].friend
|
||||
showingSheet.toggle()
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ struct ContentView: View {
|
|||
@State var isShowDismissPopup = false
|
||||
|
||||
@State var fullscreenVideo = false
|
||||
@State var isShowCallsListFragment = false
|
||||
|
||||
@State var isShowScheduleMeetingFragment = false
|
||||
|
||||
|
|
@ -913,8 +912,8 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
if telecomManager.callDisplayed && ((telecomManager.callInProgress && telecomManager.outgoingCallStarted) || telecomManager.callConnected) && !telecomManager.meetingWaitingRoomDisplayed {
|
||||
CallView(callViewModel: callViewModel, fullscreenVideo: $fullscreenVideo, isShowCallsListFragment: $isShowCallsListFragment, isShowStartCallFragment: $isShowStartCallFragment)
|
||||
.zIndex(3)
|
||||
CallView(callViewModel: callViewModel, fullscreenVideo: $fullscreenVideo, isShowStartCallFragment: $isShowStartCallFragment)
|
||||
.zIndex(5)
|
||||
.transition(.scale.combined(with: .move(edge: .top)))
|
||||
.onAppear {
|
||||
callViewModel.resetCallView()
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ struct ConversationsListFragment: View {
|
|||
}
|
||||
.padding(.trailing, 10)
|
||||
}
|
||||
.frame(height: 50)
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ struct HistoryListFragment: View {
|
|||
} else {
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
} else {
|
||||
|
|
@ -69,7 +69,7 @@ struct HistoryListFragment: View {
|
|||
? historyListViewModel.callLogs[index].toAddress!.displayName!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
|
||||
} else if historyListViewModel.callLogs[index].toAddress!.username != nil {
|
||||
|
|
@ -79,7 +79,7 @@ struct HistoryListFragment: View {
|
|||
? historyListViewModel.callLogs[index].toAddress!.username!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
} else {
|
||||
VStack {
|
||||
|
|
@ -89,7 +89,7 @@ struct HistoryListFragment: View {
|
|||
.frame(width: 28, height: 28)
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
}
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.grayMain2c200)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ struct HistoryListFragment: View {
|
|||
? historyListViewModel.callLogs[index].fromAddress!.displayName!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
} else if historyListViewModel.callLogs[index].fromAddress!.username != nil {
|
||||
Image(uiImage: contactsManager.textToImage(
|
||||
|
|
@ -110,7 +110,7 @@ struct HistoryListFragment: View {
|
|||
? historyListViewModel.callLogs[index].fromAddress!.username!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
} else {
|
||||
VStack {
|
||||
|
|
@ -120,14 +120,14 @@ struct HistoryListFragment: View {
|
|||
.frame(width: 28, height: 28)
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
}
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.grayMain2c200)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
} else {
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ struct HistoryListFragment: View {
|
|||
.frame(width: 28, height: 28)
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
}
|
||||
.frame(width: 45, height: 45)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.grayMain2c200)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
|
@ -215,8 +215,9 @@ struct HistoryListFragment: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 50)
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20))
|
||||
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
|
|
|
|||
|
|
@ -23,11 +23,16 @@ struct MeetingsFragment: View {
|
|||
ForEach(0..<meetingsListViewModel.meetingsList.count, id: \.self) { index in
|
||||
HStack {
|
||||
HStack {
|
||||
Image("users-three-square")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.frame(width: 28, height: 28)
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
VStack {
|
||||
Image("users-three-square")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.frame(width: 28, height: 28)
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
}
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.grayMain2c200)
|
||||
.clipShape(Circle())
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Text(meetingsListViewModel.meetingsList[index].model?.subject ?? "")
|
||||
|
|
@ -38,8 +43,9 @@ struct MeetingsFragment: View {
|
|||
}
|
||||
.frame(height: 40)
|
||||
}
|
||||
.frame(height: 50)
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20))
|
||||
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue