Add presence icon for Avatar

This commit is contained in:
Benoit Martins 2023-11-10 15:15:43 +01:00
parent ada6065b21
commit 5cf66f2853
16 changed files with 343 additions and 89 deletions

View file

@ -29,6 +29,8 @@
D72343362AD037AF009AA24E /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343352AD037AF009AA24E /* ToastView.swift */; };
D72992392ADD7F68003AF125 /* HistoryContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */; };
D732A9092AFD235500DB42BA /* ShareSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9082AFD235500DB42BA /* ShareSheetController.swift */; };
D732A90C2B0376F500DB42BA /* linphonerc-default in Resources */ = {isa = PBXBuildFile; fileRef = D732A90A2B0376F500DB42BA /* linphonerc-default */; };
D732A90D2B0376F500DB42BA /* linphonerc-factory in Resources */ = {isa = PBXBuildFile; fileRef = D732A90B2B0376F500DB42BA /* linphonerc-factory */; };
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; };
D748BF2E2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */; };
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */; };
@ -45,6 +47,7 @@
D7A03FC02ACC2E390081A588 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBF2ACC2E390081A588 /* HistoryView.swift */; };
D7A03FC62ACC458A0081A588 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FC52ACC458A0081A588 /* SplashScreen.swift */; };
D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */; };
D7ADF6002AFE356400212231 /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADF5FF2AFE356400212231 /* Avatar.swift */; };
D7B5066D2AEFA9B900CEB4E9 /* ContactInnerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B5066C2AEFA9B900CEB4E9 /* ContactInnerFragment.swift */; };
D7C365082AEFAB7F00FE6142 /* ContactListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */; };
D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365092AF001C300FE6142 /* EditContactFragment.swift */; };
@ -95,6 +98,8 @@
D72343352AD037AF009AA24E /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryContactFragment.swift; sourceTree = "<group>"; };
D732A9082AFD235500DB42BA /* ShareSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheetController.swift; sourceTree = "<group>"; };
D732A90A2B0376F500DB42BA /* linphonerc-default */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "linphonerc-default"; sourceTree = "<group>"; };
D732A90B2B0376F500DB42BA /* linphonerc-factory */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "linphonerc-factory"; sourceTree = "<group>"; };
D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = "<group>"; };
D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountWarningFragment.swift; sourceTree = "<group>"; };
D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage1Fragment.swift; sourceTree = "<group>"; };
@ -112,6 +117,7 @@
D7A03FC52ACC458A0081A588 /* SplashScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = "<group>"; };
D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedMainViewModel.swift; sourceTree = "<group>"; };
D7A2EDDA2AC19EEC005D90FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
D7ADF5FF2AFE356400212231 /* Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = "<group>"; };
D7B5066C2AEFA9B900CEB4E9 /* ContactInnerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactInnerFragment.swift; sourceTree = "<group>"; };
D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListBottomSheet.swift; sourceTree = "<group>"; };
D7C365092AF001C300FE6142 /* EditContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactFragment.swift; sourceTree = "<group>"; };
@ -165,6 +171,7 @@
D7C3650D2AF15BF200FE6142 /* PhotoPicker.swift */,
D7C48DF32AFA66F900D938CB /* EditContactController.swift */,
D732A9082AFD235500DB42BA /* ShareSheetController.swift */,
D7ADF5FF2AFE356400212231 /* Avatar.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -201,6 +208,7 @@
D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */,
D719ABBD2ABC67BF00B41C10 /* Preview Content */,
D7D24D0C2AC1B4C700C6F35B /* Fonts */,
D7ADF6012AFE5C7C00212231 /* Ressources */,
);
path = Linphone;
sourceTree = "<group>";
@ -375,6 +383,15 @@
path = Viewmodel;
sourceTree = "<group>";
};
D7ADF6012AFE5C7C00212231 /* Ressources */ = {
isa = PBXGroup;
children = (
D732A90A2B0376F500DB42BA /* linphonerc-default */,
D732A90B2B0376F500DB42BA /* linphonerc-factory */,
);
path = Ressources;
sourceTree = "<group>";
};
D7D24D0C2AC1B4C700C6F35B /* Fonts */ = {
isa = PBXGroup;
children = (
@ -469,6 +486,8 @@
D719ABBF2ABC67BF00B41C10 /* Preview Assets.xcassets in Resources */,
D719ABBB2ABC67BF00B41C10 /* Assets.xcassets in Resources */,
D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */,
D732A90C2B0376F500DB42BA /* linphonerc-default in Resources */,
D732A90D2B0376F500DB42BA /* linphonerc-factory in Resources */,
D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -503,6 +522,7 @@
buildActionMask = 2147483647;
files = (
D7C3650E2AF15BF200FE6142 /* PhotoPicker.swift in Sources */,
D7ADF6002AFE356400212231 /* Avatar.swift in Sources */,
D71707202AC5989C0037746F /* TextExtension.swift in Sources */,
D719ABB92ABC67BF00B41C10 /* ContentView.swift in Sources */,
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */,

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "presence-busy.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,5 @@
<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Pastille de pr&#195;&#169;sence">
<circle id="Ellipse 2" cx="11.7222" cy="11.8889" r="10.1111" fill="#DBB820" stroke="white" stroke-width="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 259 B

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "presence-online.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,5 @@
<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Pastille de pr&#195;&#169;sence">
<circle id="Ellipse 2" cx="11.7224" cy="11.8889" r="10.1111" fill="#4FAE80" stroke="white" stroke-width="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 259 B

View file

@ -30,6 +30,7 @@ final class CoreContext: ObservableObject {
@Published var loggingInProgress: Bool = false
@Published var toastMessage: String = ""
@Published var defaultAccount: Account?
@Published var coreIsStarted: Bool = false
private var mCore: Core!
private var mIteratePublisher: AnyCancellable?
@ -53,17 +54,39 @@ final class CoreContext: ObservableObject {
coreQueue.async {
let configDir = Factory.Instance.getConfigDir(context: nil)
try? self.mCore = Factory.Instance.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil)
let url = NSURL(fileURLWithPath: configDir)
if let pathComponent = url.appendingPathComponent("linphonerc") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: filePath) {
let path = Bundle.main.path(forResource: "linphonerc-default", ofType: nil)
if path != nil {
try? FileManager.default.copyItem(at: NSURL(fileURLWithPath: path!) as URL, to: pathComponent)
}
}
}
let config: Config! = Config.newForSharedCore(
appGroupId: "group.org.linphone.phone.msgNotification",
configFilename: "linphonerc",
factoryConfigFilename: Bundle.main.path(forResource: "linphonerc-factory", ofType: nil)
)
self.mCore = try? Factory.Instance.createCoreWithConfig(config: config, systemContext: nil)
self.mCore.autoIterateEnabled = false
self.mCore.friendsDatabasePath = "\(configDir)/friends.db"
self.mCore.publisher?.onGlobalStateChanged?.postOnMainQueue { (cbVal: (core: Core, state: GlobalState, message: String)) in
if cbVal.state == GlobalState.On {
self.defaultAccount = self.mCore.defaultAccount
self.coreIsStarted = true
} else if cbVal.state == GlobalState.Off {
self.defaultAccount = nil
}
}
try? self.mCore.start()
// Create a Core listener to listen for the callback we need

View file

@ -2,9 +2,15 @@
<!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.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.belledonne-communications.linphone</string>
<string>group.org.linphone.phone.msgNotification</string>
<string>group.org.linphone.phone.linphoneExtension</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>

View file

@ -29,7 +29,7 @@ struct LinphoneApp: App {
var body: some Scene {
WindowGroup {
if isActive {
if isActive && coreContext.coreIsStarted {
if !sharedMainViewModel.welcomeViewDisplayed {
WelcomeView(sharedMainViewModel: sharedMainViewModel)
} else if coreContext.defaultAccount == nil || sharedMainViewModel.displayProfileMode {

View file

@ -0,0 +1,39 @@
## Start of default rc
[sip]
contact="Linphone iPhone" <sip:linphone.iphone@unknown-host>
use_info=0
use_ipv6=1
keepalive_period=30000
sip_port=-1
sip_tcp_port=-1
sip_tls_port=-1
media_encryption=none
update_presence_model_timestamp_before_publish_expires_refresh=1
[net]
#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
download_bw=0
upload_bw=0
[video]
size=vga
[app]
tunnel=disabled
auto_start=1
record_aware=1
[tunnel]
host=
port=443
[misc]
log_collection_upload_server_url=https://www.linphone.org:444/lft.php
file_transfer_server_url=https://www.linphone.org:444/lft.php
version_check_url_root=https://www.linphone.org/releases
max_calls=10
conference_layout=1
## End of default rc

View file

@ -0,0 +1,65 @@
## Start of factory rc
# This file shall not contain path referencing package name, in order to be portable when app is renamed.
# Paths to resources must be set from LinphoneManager, after creating LinphoneCore.
[net]
mtu=1300
force_ice_disablement=0
[rtp]
accept_any_encryption=1
[sip]
guess_hostname=1
register_only_when_network_is_up=1
auto_net_state_mon=1
auto_answer_replacing_calls=1
ping_with_options=0
use_cpim=1
zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512
chat_messages_aggregation_delay=1000
chat_messages_aggregation=1
update_presence_model_timestamp_before_publish_expires_refresh=1
rls_uri=sips:rls@sip.linphone.org
[sound]
#remove this property for any application that is not Linphone public version itself
ec_calibrator_cool_tones=1
[video]
displaytype=MSAndroidTextureDisplay
auto_resize_preview_to_keep_ratio=1
max_conference_size=vga
[misc]
enable_basic_to_client_group_chat_room_migration=0
enable_simple_group_chat_message_state=0
aggregate_imdn=1
notify_each_friend_individually_when_presence_received=0
store_friends=0
[app]
activation_code_length=4
prefer_basic_chat_room=1
record_aware=1
[account_creator]
backend=1
# 1 means FlexiAPI, 0 is XMLRPC
url=https://subscribe.linphone.org/api/
# replace above URL by https://staging-subscribe.linphone.org/api/ for testing
[lime]
lime_update_threshold=86400
[alerts]
alerts_enabled=1
[assistant]
algorithm=SHA-256
password_min_length=6
username_regex=^[a-z0-9+_.\-]*$
## End of factory rc

View file

@ -113,28 +113,9 @@ struct ContactInnerFragment: View {
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo!.isEmpty {
AsyncImage(
url: ContactsManager.shared.getImagePath(
friendPhotoPath: magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 100, height: 100)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(friend: magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!, avatarSize: 100)
} else if contactViewModel.indexDisplayedFriend != nil && magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
Image("profil-picture-default")
.resizable()

View file

@ -64,26 +64,9 @@ struct ContactsListFragment: View {
}
if magicSearch.lastSearch[index].friend!.photo != nil && !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: magicSearch.lastSearch[index].friend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 45, height: 45)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 45, height: 45)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(friend: magicSearch.lastSearch[index].friend!, avatarSize: 45)
} else {
Image("profil-picture-default")
.resizable()

View file

@ -129,26 +129,9 @@ struct EditContactFragment: View {
if editContactViewModel.selectedEditFriend != nil
&& editContactViewModel.selectedEditFriend!.photo != nil
&& !editContactViewModel.selectedEditFriend!.photo!.isEmpty && selectedImage == nil && !removedImage {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: editContactViewModel.selectedEditFriend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 100, height: 100)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(friend: editContactViewModel.selectedEditFriend!, avatarSize: 100)
} else if selectedImage == nil {
Image("profil-picture-default")
.resizable()

View file

@ -39,27 +39,9 @@ struct FavoriteContactsListFragment: View {
VStack {
if magicSearch.lastSearch[index].friend!.photo != nil
&& !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: magicSearch.lastSearch[index].friend!.photo!)
) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 45, height: 45)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 45, height: 45)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(friend: magicSearch.lastSearch[index].friend!, avatarSize: 45)
} else {
Image("profil-picture-default")
.resizable()

View file

@ -136,6 +136,7 @@ struct ContentView: View {
Menu {
Button {
isMenuOpen = false
contactViewModel.indexDisplayedFriend = nil
magicSearch.allContact = true
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
@ -153,6 +154,7 @@ struct ContentView: View {
Button {
isMenuOpen = false
contactViewModel.indexDisplayedFriend = nil
magicSearch.allContact = false
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)

118
Linphone/Utils/Avatar.swift Normal file
View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2010-2023 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
import linphonesw
struct Avatar: View {
var friend: Friend
let avatarSize: CGFloat
@State private var friendDelegate: FriendDelegate?
@State private var presenceImage = ""
var body: some View {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: friend.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: avatarSize, height: avatarSize)
case .success(let image):
ZStack {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
HStack {
Spacer()
VStack {
Spacer()
if !friend.addresses.isEmpty {
if presenceImage.isEmpty && (friend.consolidatedPresence == ConsolidatedPresence.Online || friend.consolidatedPresence == ConsolidatedPresence.Busy) {
Image(friend.consolidatedPresence == ConsolidatedPresence.Online ? "presence-online" : "presence-busy")
.resizable()
.frame(width: avatarSize/4, height: avatarSize/4)
.padding(.trailing, avatarSize == 45 ? 1 : 3)
.padding(.bottom, avatarSize == 45 ? 1 : 3)
} else if !presenceImage.isEmpty && (friend.consolidatedPresence != ConsolidatedPresence.DoNotDisturb || friend.consolidatedPresence != ConsolidatedPresence.Offline) {
Image(presenceImage)
.resizable()
.frame(width: avatarSize/4, height: avatarSize/4)
.padding(.trailing, avatarSize == 45 ? 1 : 3)
.padding(.bottom, avatarSize == 45 ? 1 : 3)
}
}
}
}
.frame(width: avatarSize, height: avatarSize)
}
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
.onAppear {
addDelegate()
}
.onDisappear {
removeAllDelegate()
}
}
func addDelegate() {
if friend.address != nil {
print("Presence received for first \(friend.name!) \(friend.consolidatedPresence)")
//self.presenceImage = friend.consolidatedPresence == ConsolidatedPresence.Online ? "presence-online" : "presence-busy"
}
let newFriendDelegate = FriendDelegateStub(
onPresenceReceived: { (linphoneFriend: Friend) -> Void in
print("Presence received for second \(linphoneFriend.name) \(linphoneFriend.consolidatedPresence)")
/*
if linphoneFriend.address != nil && friend.address != nil
&& linphoneFriend.address!.asStringUriOnly() == friend.address!.asStringUriOnly() {
let presenceModel = linphoneFriend.getPresenceModelForUriOrTel(uriOrTel: (linphoneFriend.address!.asStringUriOnly()))
if presenceModel != nil {
presenceImage = presenceModel!.consolidatedPresence == ConsolidatedPresence.Online ? "presence-online" : "presence-busy"
}
}
*/
}
)
friendDelegate = newFriendDelegate
if friendDelegate != nil {
friend.addDelegate(delegate: friendDelegate!)
}
}
func removeAllDelegate(){
if friendDelegate != nil {
presenceImage = ""
friend.removeDelegate(delegate: friendDelegate!)
friendDelegate = nil
}
}
}