Add emoji picker

This commit is contained in:
Benoit Martins 2025-07-11 11:16:46 +02:00
parent cfcd55a622
commit c6179c973c
6 changed files with 316 additions and 157 deletions

View file

@ -47,6 +47,8 @@ class ContactAvatarModel: ObservableObject, Identifiable {
private var friendDelegate: FriendDelegate?
init(friend: Friend?, name: String, address: String, withPresence: Bool?) {
self.name = name
self.address = address
self.resetContactAvatarModel(friend: friend, name: name, address: address, withPresence: withPresence)
}

View file

@ -19,6 +19,7 @@
import SwiftUI
import UniformTypeIdentifiers
import ElegantEmojiPicker
// swiftlint:disable line_length
// swiftlint:disable type_body_length
@ -81,6 +82,10 @@ struct ConversationFragment: View {
@State var messageText: String = ""
@State private var chosen: String?
@State private var showPicker = false
@State private var isSheetVisible = false
var body: some View {
NavigationView {
GeometryReader { geometry in
@ -141,6 +146,17 @@ struct ConversationFragment: View {
})
.edgesIgnoringSafeArea(.all)
})
.sheet(isPresented: $showPicker) {
EmojiPickerView(selected: $chosen, isSheetVisible: $isSheetVisible)
.presentationDetents([.medium])
.edgesIgnoringSafeArea(.all)
}
.onChange(of: chosen ?? "") { newValue in
if !newValue.isEmpty {
conversationViewModel.sendReaction(emoji: newValue)
chosen = nil
}
}
.fullScreenCover(isPresented: $isShowCamera) {
ImagePicker(selectedMedia: self.$conversationViewModel.mediasToSend)
.environmentObject(conversationViewModel)
@ -185,6 +201,16 @@ struct ConversationFragment: View {
}
.edgesIgnoringSafeArea(.all)
})
.sheet(isPresented: $showPicker) {
EmojiPickerView(selected: $chosen, isSheetVisible: $isSheetVisible)
.edgesIgnoringSafeArea(.all)
}
.onChange(of: chosen ?? "") { newValue in
if !newValue.isEmpty {
conversationViewModel.sendReaction(emoji: newValue)
chosen = nil
}
}
.fullScreenCover(isPresented: $isShowCamera) {
ImagePicker(selectedMedia: self.$conversationViewModel.mediasToSend)
.environmentObject(conversationViewModel)
@ -892,6 +918,7 @@ struct ConversationFragment: View {
Spacer()
VStack {
if !isSheetVisible {
HStack {
if conversationViewModel.selectedMessage!.message.isOutgoing {
Spacer()
@ -948,17 +975,20 @@ struct ConversationFragment: View {
.background(conversationViewModel.selectedMessage?.message.ownReaction == "😢" ? Color.gray200 : .white)
.cornerRadius(10)
/*
Button {
showPicker = true
isSheetVisible = true
} label: {
Image("plus-circle")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c500)
.frame(width: iconSize > 50 ? 50 : iconSize, height: iconSize > 50 ? 50 : iconSize, alignment: .leading)
.frame(width: iconSize > 50 ? 55 : iconSize + 5, height: iconSize > 50 ? 55 : iconSize + 5, alignment: .leading)
}
.padding(.trailing, 5)
*/
.padding(.top, 3)
.padding(.leading, 2)
.padding(.trailing, 6)
.cornerRadius(10)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
@ -973,13 +1003,16 @@ struct ConversationFragment: View {
.padding(.horizontal, 10)
.padding(.leading, SharedMainViewModel.shared.displayedConversation!.isGroup ? 43 : 0)
.shadow(color: .black.opacity(0.1), radius: 10)
}
ChatBubbleView(eventLogMessage: conversationViewModel.selectedMessage!, geometryProxy: geometry)
.environmentObject(conversationViewModel)
.padding(.horizontal, 10)
.padding(.vertical, 1)
.shadow(color: .black.opacity(0.1), radius: 10)
.offset(y: isSheetVisible ? -(UIScreen.main.bounds.height * 0.5) - 10 : 0)
if !isSheetVisible {
HStack {
if conversationViewModel.selectedMessage!.message.isOutgoing {
Spacer()
@ -1085,6 +1118,7 @@ struct ConversationFragment: View {
.shadow(color: .black.opacity(0.1), radius: 10)
}
}
}
.frame(maxWidth: .infinity)
.frame(minHeight: geometry.size.height)
}

View file

@ -2261,8 +2261,15 @@ class ConversationViewModel: ObservableObject {
if chatMessageReaction.fromAddress != nil {
dispatchGroup.enter()
ContactAvatarModel.getAvatarModelFromAddress(address: chatMessageReaction.fromAddress!) { avatarResult in
if core.defaultAccount != nil && core.defaultAccount!.contactAddress != nil && core.defaultAccount!.contactAddress!.asStringUriOnly().contains(avatarResult.address) {
let innerSheetCat = InnerSheetCategory(contact: avatarResult, detail: chatMessageReaction.body, isMe: true)
if let account = core.defaultAccount,
let contactAddress = account.contactAddress,
contactAddress.asStringUriOnly().contains(avatarResult.address) {
let innerSheetCat = InnerSheetCategory(
contact: avatarResult,
detail: chatMessageReaction.body,
isMe: true
)
participantList[0].append(innerSheetCat)
} else {
let innerSheetCat = InnerSheetCategory(contact: avatarResult, detail: chatMessageReaction.body)

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2010-2024 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 ElegantEmojiPicker
struct EmojiPickerView: UIViewControllerRepresentable {
@Binding var selected: String?
@Binding var isSheetVisible: Bool
var configuration = ElegantConfiguration(showRandom: false, showReset: false)
func makeUIViewController(context: Context) -> UIViewController {
let picker = ElegantEmojiPicker(delegate: context.coordinator,
configuration: configuration,
localization: .init())
let container = NotifyingViewController()
container.onWillDisappear = {
isSheetVisible = false
}
container.addChild(picker)
container.view.addSubview(picker.view)
picker.view.frame = container.view.bounds
picker.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
picker.didMove(toParent: container)
return container
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class NotifyingViewController: UIViewController {
var onWillDisappear: (() -> Void)?
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
onWillDisappear?()
}
}
final class Coordinator: NSObject, ElegantEmojiPickerDelegate {
let parent: EmojiPickerView
init(_ parent: EmojiPickerView) { self.parent = parent }
func emojiPicker(_ picker: ElegantEmojiPicker, didSelectEmoji emoji: Emoji?) {
parent.selected = emoji?.emoji
picker.dismiss(animated: true)
}
func emojiPicker(_ picker: ElegantEmojiPicker,
loadEmojiSections withConfiguration: ElegantConfiguration,
_ withLocalization: ElegantLocalization) -> [EmojiSection] {
return ElegantEmojiPicker.getDefaultEmojiSections()
}
}
}

View file

@ -182,6 +182,8 @@
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; };
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */; };
D7DC09712CFDBF9A00A6D47C /* AccountProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */; };
D7DF8BE72E2104DC003A3BC7 /* ElegantEmojiPicker in Frameworks */ = {isa = PBXBuildFile; productRef = D7DF8BE62E2104DC003A3BC7 /* ElegantEmojiPicker */; };
D7DF8BE92E2104EC003A3BC7 /* EmojiPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DF8BE82E2104E5003A3BC7 /* EmojiPickerView.swift */; };
D7E2E69F2CE356C90080DA0D /* PopupViewWithTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */; };
D7E394C52DAC6561005FA0DD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D737AEED2DA011F2005C1280 /* Localizable.strings */; };
D7E6ADF32B9875C20009A2BC /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF22B9875C20009A2BC /* Message.swift */; };
@ -400,6 +402,7 @@
D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = "<group>"; };
D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileFragment.swift; sourceTree = "<group>"; };
D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileViewModel.swift; sourceTree = "<group>"; };
D7DF8BE82E2104E5003A3BC7 /* EmojiPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerView.swift; sourceTree = "<group>"; };
D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupViewWithTextField.swift; sourceTree = "<group>"; };
D7E6ADF22B9875C20009A2BC /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
D7E6ADF42B9876ED0009A2BC /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
@ -445,6 +448,7 @@
buildActionMask = 2147483647;
files = (
D7D5AD7F2DD34F0E00016721 /* AppAuth in Frameworks */,
D7DF8BE72E2104DC003A3BC7 /* ElegantEmojiPicker in Frameworks */,
C618BF562D75CA03005A00E0 /* linphonesw in Frameworks */,
D7D5AD832DD34F2300016721 /* FirebaseCrashlytics in Frameworks */,
D7D5AD812DD34F1A00016721 /* FirebaseAnalytics in Frameworks */,
@ -575,6 +579,7 @@
D717071C2AC591EF0037746F /* Utils */ = {
isa = PBXGroup;
children = (
D7DF8BE82E2104E5003A3BC7 /* EmojiPickerView.swift */,
D703F7072DC8C5FF005B8F75 /* FilePicker.swift */,
D717A10D2CEB770D00849D92 /* ShareSheetController.swift */,
66C491F72B24D25A00CEA16D /* Extensions */,
@ -602,6 +607,7 @@
D719ABB52ABC67BF00B41C10 /* Linphone */,
660AAF7C2B839272004C0FA6 /* msgNotificationService */,
D7458F302E0BDCF4000C957A /* linphoneExtension */,
D7DF8BE52E2104DC003A3BC7 /* Frameworks */,
D719ABB42ABC67BF00B41C10 /* Products */,
);
sourceTree = "<group>";
@ -1039,6 +1045,13 @@
path = ViewModel;
sourceTree = "<group>";
};
D7DF8BE52E2104DC003A3BC7 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -1140,6 +1153,7 @@
D7D5AD7B2DD34E4D00016721 /* XCRemoteSwiftPackageReference "AppAuth-iOS" */,
D7D5AD7C2DD34E7C00016721 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
D7BEB1152E1FF670004B25A3 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */,
D7DF8BE42E2104D0003A3BC7 /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */,
);
productRefGroup = D719ABB42ABC67BF00B41C10 /* Products */;
projectDirPath = "";
@ -1244,6 +1258,7 @@
D7B5678E2B28888F00DE63EB /* CallView.swift in Sources */,
D71A0E192B485ADF0002C6CD /* ViewExtension.swift in Sources */,
D759CB642C3FBD4200AC35E8 /* StartConversationFragment.swift in Sources */,
D7DF8BE92E2104EC003A3BC7 /* EmojiPickerView.swift in Sources */,
66FDB7812C7C689A00561566 /* EventEditViewController.swift in Sources */,
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */,
D7E6D0492AE933AD00A57AAF /* FavoriteContactsListFragment.swift in Sources */,
@ -1889,6 +1904,14 @@
minimumVersion = 11.12.0;
};
};
D7DF8BE42E2104D0003A3BC7 /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Finalet/Elegant-Emoji-Picker";
requirement = {
branch = main;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@ -1927,6 +1950,11 @@
package = D7D5AD7C2DD34E7C00016721 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseCrashlytics;
};
D7DF8BE62E2104DC003A3BC7 /* ElegantEmojiPicker */ = {
isa = XCSwiftPackageProductDependency;
package = D7DF8BE42E2104D0003A3BC7 /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */;
productName = ElegantEmojiPicker;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = D719ABAB2ABC67BF00B41C10 /* Project object */;

View file

@ -1,5 +1,5 @@
{
"originHash" : "5293adb495d47691babe33d739eb8e74cee00c05a442b57ef6079701fee324f1",
"originHash" : "7ed0929d53447e6dba6aae5d5c6617250fb4460459e56cf06feaf0f1397ae64e",
"pins" : [
{
"identity" : "abseil-cpp-binary",
@ -28,6 +28,15 @@
"version" : "2.0.0"
}
},
{
"identity" : "elegant-emoji-picker",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Finalet/Elegant-Emoji-Picker",
"state" : {
"branch" : "main",
"revision" : "12c1a2be1adbe7a774ebdd2c48f02d95b8884df6"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",